龙空技术网

包教包会!C语言 第一课编译运行hello,world程序

蓝戰科技 172

前言:

如今看官们对“复杂c语言程序”可能比较重视,朋友们都需要剖析一些“复杂c语言程序”的相关知识。那么小编在网摘上汇集了一些有关“复杂c语言程序””的相关内容,希望咱们能喜欢,兄弟们快快来了解一下吧!

技术要求

您需要一台运行计算机,具有以下功能:

• 一个基本的文本编辑器,可以保存未格式化的纯文本

• 一个终端窗口,可以通过命令行输入命令

• 一个编译器,用于构建您的C程序。

每个将在本章中遇到的工具将在更详细地讲解。

本章的源代码可以在上找到。然而,请尽最大努力自己输入源代码。即使一开始您可能会感到沮丧,但如果您自己输入所有代码,您将学习得更多,而且学习得更快。

编写您的第一个C程序

我们将从使用C创建的最简单、最有用的程序之一开始。该程序首次由其创建者Brian W. Kernighan和Dennis M. Ritchie在他们的经典作品《C程序设计语言》中使用,该书于1978年出版。该程序在计算机屏幕上打印出一行输出——问候语Hello, world!。

这个简单的程序之所以重要,有几个原因。首先,它让我们了解了C程序的风格,但更重要的是,它证明了开发环境的必要组成部分——操作系统(OS)、文本编辑器、命令行界面和编译器——在您的计算机系统上正确安装并正常工作。最后,它让我们第一次体验了基本的编程开发周期。在学习编程和后来实际解决实际问题的过程中,您将经常重复这个周期。您必须熟悉和适应这个周期才行。

这个程序很有用,因为它向终端输出了一些内容,告诉我们它实际上做了些什么——向我们显示了一条消息。我们可以用C编写更短的程序,但它们不会有太大的用处。虽然我们可以构建和运行它们,但我们几乎没有证据表明任何事情实际上发生了。所以,这就是您的第一个C程序。在本书的整个过程中,以及在您的编程经验中,获取实际发生的事情的证据至关重要。

自从Kernighan和Ritchie在40年前介绍了Hello, world!程序以来,这个简单的程序已被重新用于介绍许多编程语言,并在各种环境中使用。您可以在Java、C++、Objective-C、Python、Ruby等语言中找到此程序的变体。甚至GitHub,一个在线源代码存储库,也使用Hello World的入门指南介绍其网站和功能。

Hello, world!

话不多说,下面是Hello, world!的C程序。它不执行任何计算,也不接受任何输入。它只显示一个简短的问候语,然后结束,如下所示:

#include <stdio.h>

int main()

{

printf( "Hello, world!\n" );

return 0;

}

自此该程序的某些细节已经发生了变化。此处呈现的内容将可以使用过去30年内创建的所有C编译器来构建和运行。

在我们深入了解程序的每个部分所做的事情之前,请看看能否确定程序的哪一行打印了我们的问候语。您可能会发现标点符号很奇怪;我们将在下一章中解释这一点。此外,请注意一些标点符号是成对出现的,而其他标点符号则不是。总共有五对和五个未成对的标点符号。你能找出它们吗?(请注意,我们没有将标点符号,即逗号和感叹号,计入Hello, world!消息中。)

在这个简单程序中,还有另一对匹配不是那么明显,但我们将在下一章中进一步探讨它。作为提示,这对匹配涉及到int main()和return 0;这两行。

在我们开始创建、编译和运行此程序之前,我们需要概述整个开发过程以及我们将要使用的工具。

提示

如果您渴望开始创建您的第一个程序,可以跳到下一节。如果您这样做,请回到了解程序开发周期部分以完成您的理解。

了解程序开发周期

有两种主要的开发环境:

• 解释型:在解释型环境(例如Python或Ruby)中,程序可以逐行输入并在任何时候运行。每行在输入时都会被评估和执行,结果立即返回到控制台。解释型环境是动态的,因为它们提供即时反馈,适用于快速探索算法和程序特性。在此输入的程序往往需要解释环境也在运行中。

• 编译型:在编译环境中,如C、C++、C#或Objective-C,程序被输入到一个或多个文件中,然后全部编译,如果没有错误,则可以作为一个整体运行。每个阶段都是独立的,使用不同的程序。编译后的程序 tend to执行更快,因为有一个单独的完整编译阶段,可以独立于解释环境运行。

与洗发水一样,在我们习惯了湿发、起泡、冲洗和重复的过程中,我们将以同样的方式熟悉C——编辑、编译、运行、验证和重复。

编辑

程序是从使用预定义文件扩展名的文本文件生成的。这些称为源文件或源代码文件。对于C,.c文件扩展名表示C源代码文件。.h扩展名(在我们的Hello, world!程序中存在)表示C头文件。编译器在遇到它们时查找.c和.h文件,并且由于每个文件具有不同的目的,它也以不同的方式处理每个文件。其他语言有自己的文件扩展名;源代码文件的内容应该与编译器所期望的语言匹配。

要创建和修改C文件,您需要一个纯文本编辑器。这是一个程序,允许您打开、修改和保存纯文本,而没有任何格式,如字体大小、字体系列和字体样式。例如,在Windows上,Notepad是一个纯文本编辑器,而Word则不是。纯文本编辑器应具有以下功能:

• 文件操作:这允许您打开文件、编辑文件、保存文件和对其进行任何更改,最后将文件保存为另一个名称。

• 浏览文件的能力:这使您可以向上、向下、向左或向右移动到行的开头、行的结尾、文件的开头、文件的结尾等位置。

• 文本操作:这允许您插入文本、删除文本、插入行、删除行、选择、剪切、复制、粘贴、撤消/重做等操作。

• 搜索和替换:这允许您查找文本、替换文本等。

以下功能很方便但不是必需的:

• 自动缩

• 针对特定编程语言的语法着色

• 自动定期保存

几乎任何纯文本编辑器都可以。不要过于纠结于某个特定文本编辑器的功能。有些编辑器比其他编辑器好;有些是免费的,而有些是昂贵的,可能并不立即值得购买(也许稍后,一个或多个可能会值得购买,但现在不是时候),而且没有一个编辑器会百分之百满足你的需求。

以下是一些值得在电脑上安装并尝试的免费纯文本编辑器:

• 到处都可以用:Nano,它在终端窗口中运行,具有适度的学习曲线。

• Linux/Unix:包括以下内容:

a. Vim或vi:它在终端窗口中运行,具有适度的学习曲线。它在每个Linux/Unix系统中都有,因此值得学习如何使用它的基本功能。

b. gedit:这是一个功能强大的通用编辑器。

c. Emacs:这是一个拥有非常大学习曲线的全能编辑器。

• Windows:包括以下内容:

a. 记事本:这非常简单 - 有时对于编程来说太简单了 - 但包含在每个Windows系统中。

b. Notepad++:这是记事本的更好版本,具有许多针对编程的功能。

• 仅限macOS:它运行BBEdit(免费版本),这是一个功能齐全的GUI编程文本编辑器。

有许多文本编辑器,每个都有其优点和缺点。选择一个文本编辑器并习惯它。随着时间的推移,随着您的使用越来越频繁,它将变得非常自然。

编译

编译器是一个程序,它接收输入的源代码文件 - 在我们的情况下是.c和.h文件 - 将那里找到的文本源代码转换为机器语言,并链接所有预定义的部分,以使程序能够在我们特定的计算机硬件和操作系统上运行。它生成一个由机器语言组成的可执行文件。

机器语言是一系列指令和数据,特定的中央处理器(CPU)知道如何从程序执行流中获取并逐个在计算机上执行。每个CPU都有自己的机器语言或指令集。通过使用通用语言(如C)进行编程,程序员可以屏蔽机器语言的细节;那些知识被体现在编译器中。

有时汇编语言也被称为机器语言,但这并不完全准确,因为汇编语言仍包含文本和符号,而机器语言只包含二进制数字。如今,很少有人具备直接阅读机器语言的技能;曾经,更多的程序员能够做到这一点。时代已经变了!

当我们编译程序时,我们调用编译器来处理一个或多个源文件。这个调用的结果要么成功并生成可执行文件,要么会识别编译过程中发现的编程错误。编程错误可以从简单的名称拼写错误或省略标点符号到更复杂的语法错误不等。通常,编译器会尝试理解它发现的任何错误;它会尝试为发现的问题提供有用的信息。请注意,尝试和试图仅仅是目标;实际上,编译器可能会输出很多行来自单个错误的错误消息。此外,编译器将在调用时处理整个源代码。对于每个编译器调用,您可能会在程序的不同部分中发现许多不同的错误。

一个完整的可运行程序由我们编写的已编译源代码(即我们编写的代码)和随操作系统提供的预定义编译程序组成,即操作系统作者编写的代码。预定义的程序代码有时被称为运行时库。它由一组可调用的例程组成,这些例程知道如何与计算机的各个部分详细交互。例如,在Hello,world!中,我们不必知道发送字符到计算机屏幕的详细指令-我们可以简单地调用预定义函数printf();来为我们执行此操作。printf()是C运行时库的一部分,还有许多其他例程,我们稍后将发现。在下一章中,我们将探讨函数的作用。即使这些操作系统在相同的硬件上运行,将文本发送到控制台的方式可能也会有所不同。因此,程序员不仅可以屏蔽机器语言的细节,而且还可以屏蔽计算机本身不同实现的细节。

由此可以得出,针对每个操作系统,都有特定的编译器和运行时库。一个针对某个操作系统设计的编译器很可能无法在另一个操作系统上工作。如果某个操作系统的编译器碰巧或者看起来可以在另一个操作系统上运行,那么生成的程序及其执行结果会非常不可预测。这可能会引发混乱。

许多操作系统都有C编译器

你可以在许多计算机平台上学习C。在Unix和Linux操作系统上常用的编译器是GNU编译器集合(GCC)或LLVM编译器项目Clang。对于Windows,可以通过Cygwin项目或MinGW项目获得GCC。你甚至可以使用树莓派或Arduino学习C,但由于这些极简计算机系统需要特别考虑,这并不理想。建议使用台式计算机,因为任何能运行Web浏览器的台式计算机都可以获得更多的计算机资源(例如内存、硬盘空间、CPU能力等)。

重要提示

你应该知道,C 有许多变体。通常,这些是由硬件供应商为其客户的特定需求创建的,或者由其他希望保留扩展功能而未经批准的团体创建的。

此外,请注意,C 规范存在未定义的部分;也就是说,标准委员会将这些实现细节留给了特定硬件系统上给定编译器的创建者。

在本书中,我们将描述和专注于 C 标准提供的内容。然而,我们的方法将强调如何验证程序行为,以便我们始终知道我们的程序的行为差异。由于编译器是由人编写而不是从规范自动生成的,因此我们遵循这样的指导:信任但要验证。

关于集成开发环境的说明

在许多操作系统中,编译器作为该操作系统的集成开发环境(IDE)的一部分安装。IDE由一组程序组成,用于为该操作系统创建、构建和测试程序。它管理与程序相关的一个或多个文件,具有自己的集成文本编辑器,可以调用编译器并呈现其结果,并可以执行已编译的程序。通常,在开发过程中,程序员从未离开此环境。通常,IDE简化了独立工作程序的生产。

有许多这样的IDE——Microsoft的仅限Windows的Visual Studio,Microsoft的多平台Visual Studio Code,用于macOS和其他Apple硬件平台的Apple Xcode,Eclipse Foundation的Eclipse和Oracle的Netbeans等。这些IDE可以使用各种语言开发程序。本书中使用的几乎所有程序都是使用名为CodeRunner的简单IDE在macOS上开发的。

我们不会使用IDE来学习C。实际上,在您学习的这个阶段,出于几个原因,不建议使用IDE。首先,学习和使用IDE本身可能是一项令人畏惧的学习任务。这项任务可以和应该推迟到您有更多的经验掌握程序开发周期中的每个单独部分之后再开始。IDE虽然具有常见的功能,但它们有时以大不相同的方式实现,并具有太多不同的功能需要探索。先学习C,然后您可以稍后为所需的环境学习IDE。

在Linux、macOS和Windows上安装C编译器的步骤如下:

• 对于Linux,执行以下步骤:

如果您正在运行基于Red Hat软件包管理器(RPM)的Linux,例如Red Hat、Fedora或CentOS,请在命令行中输入以下命令:

$ sudo yum group install development-tools

如果您正在运行Debian Linux,请在终端窗口中输入以下命令:

$ sudo apt-get install build-essential

在命令行中输入以下命令以验证安装:

$ cc --version

从前面的命令中,您会发现您可能有GCC或Clang,两者都可以。现在,您已准备好在您的Linux版本上编译C程序。

• 对于macOS,执行以下步骤:

打开Terminal.app并在命令行中输入以下内容:

$ cc --version

如果尚未安装开发工具,只需调用前面的命令即可引导您完成安装。

安装完成后,关闭Terminal窗口,打开新窗口并输入以下内容:

$ cc --version

现在,您已准备好在您的macOS版本上编译C程序。

• 对于Windows,执行以下步骤:

从它们各自的网站安装Cygwin()或MinGW()。两者都可以正常工作。如果您选择安装Cygwin,请确保还安装了GCC的额外包。这将安装许多其他必要的编译器和调试程序。

安装完成后,打开命令提示符并输入以下内容:

$ cc --version

现在,您已准备好在您的Windows版本上编译C程序。

请注意,编译是一个两部分的过程——编译和链接。编译涉及语法检查和将源代码转换为几乎完整的可执行代码。在链接阶段,几乎完整的机器代码将与运行时库合并并成为完整的代码。通常情况下,当我们调用编译器时,链接器也会被调用。如果编译阶段成功(即没有错误),链接阶段将自动被调用。稍后,我们将发现我们可以在编译时——编译阶段——或链接时——链接阶段——从编译器获得错误消息,当程序的所有部分被链接在一起时。

我们将在稍后学习如何调用编译器,当我们编译第一个程序时。

在本书中,一旦您拥有了一个可运行的程序,我们将指导您故意破坏它——导致编译您的程序失败。这样您就可以开始了解各种程序错误与编译器错误之间的相关性,这样您就不会害怕破坏您的程序。您只需撤消更改,成功将再次属于您。

运行

一旦成功完成编译,将生成一个可执行文件。这个可执行文件(除非我们为其提供了一个显式的名称)将被命名为a.out。通常情况下,可执行文件将被创建在调用编译器的目录中。在大多数情况下,我们将确保当前工作目录与源文件的位置相同。

运行可执行文件是通过从命令行调用它来完成的。当被调用时,可执行文件被加载到计算机的内存中,然后成为CPU的程序执行流。一旦加载到内存中,CPU就会从特殊保留字main()开始执行,直到遇到return;或一个闭合}字符。程序停止,可执行文件然后被从内存中卸载。

要运行可执行文件,请打开命令提示符(在Windows上)或终端窗口(在Linux和Mac上),使用cd导航到可执行文件所在的目录,然后只需输入可执行文件的名称(例如a.out或您指定的任何名称)。

注意

如果您成功地导航到与可执行文件相同的位置并验证其存在,但从命令解释器收到错误消息,则可能存在与命令解释器内置的PATH变量有关的问题。为了快速解决此问题,输入$./a.out命令来运行它。这将指示命令解释器在当前目录中查找名为a.out的文件。当程序运行时,任何输出都将被指向到终端或控制台窗口。当程序结束时,命令解释器将会给你一个新的命令提示符。

验证

在程序开发的过程中,你可能会认为只要成功编译并且运行程序没有导致计算机崩溃,就已经完成了。然而,你还没有完成。你必须验证你所认为的程序应该做的事情是它实际上做的事情。你的程序是否解决了它应该解决的问题?结果是否正确?

因此,你必须返回到编写原始程序的过程,然后将其与程序输出进行比较。如果你的预期结果匹配,那么你的程序就是正确的,这时你才完成了任务。

随着我们编写更复杂的程序,我们将发现一个良好的程序应该具备以下特点:

• 正确:程序能够做到它应该做到的事情。

• 完备:程序能够完成它应该完成的所有任务。

• 简洁:程序仅仅做它应该做的事情,并且以尽可能高效的方式完成。

• 清晰:程序易于被使用者和维护者理解。

在本书的大部分内容中,我们将主要关注正确性、完备性和清晰度。目前,hello1.c不完整,也不清晰,我们很快就会了解原因。

重复

与我们之前提到的洗发水隐喻不同的是,即使只重复一次指令,你也要多次重复此程序开发周期。

很少情况下,你能够只通过一次迭代来完成程序开发周期。最有可能的是,你不得不多次重复其中的某些部分。例如,你可能会编辑源代码,编译它,发现编译器失败了。在这种情况下,你必须返回编辑源代码并重新编译它,每次找出问题所在并进行修复。一旦成功编译,你就可以开始运行和验证它。如果输出不正确或程序崩溃,你必须找出问题所在并重新编辑源代码。

这听起来很令人沮丧吗?它确实可以 - 特别是当你不知道为什么出了问题或者你无法理解编译器或计算机给出的错误信息时。

很多年前,当编译器还比较简单,而且不像今天这样容易出错(实际上,编译器仍然不太容易原谅——它们只是变得更好了,可以更好地发现我们人类在编写程序时可能犯的错误,并以更好的方式向我们传达信息!),我第一次尝试在数字设备虚拟地址扩展(VAX)虚拟存储系统(VMS)C编译器上编译我的“Hello, world!”程序时,编译器给了我23000个错误消息。结果发现我漏掉了一个分号。一个字符。天哪!

这个故事的要点是要让你放心,你会犯错,主要是漏掉或错误的标点符号或拼错变量名等,你会感到沮丧。学习编程的一部分就是学习如何处理你的挫折感,以及如何成为一个侦探来追踪那些会出现的小错误。

当发生这种情况时,请离开电脑休息一下。出去走走。笑一笑。然后,回到工作中。不要忽略前面的部分(也就是笑一笑和走动一下)。

关于调试的一点说明

随着你逐渐熟悉开发语言、开发工具和自己(没错,编程过程中你也在学习自己),整个程序开发过程将变得自然而然。当你出现打字错误或得到明显错误的结果时,这些并不是 bug,只是普通的错误而已。而真正的 bug 更加隐蔽。

对于大多数初学编程的人来说,有一个非常难以察觉的陷阱,那就是他们对程序应该按照某种方式运行的假设,却没有证实过这一点。我自己写的代码中,最难排除的 bug 往往都是那些我以为程序应该按照某种方式工作,但我没有验证过的部分。当我最终回到我的假设并在代码中证明了它们时,我才能够克服自己制造的 bug。

那么,你能避免这个陷阱吗?

可以。在本书中,我们将采用一种方法来开发程序,试错、引导式发现和观察证据。有时,我们会有意破坏程序,看看会发生什么。此外,我们将尝试证明每个概念,以便期望的行为与实际行为相匹配。

这并不是说,即使采用这样的方法,bug 也不会出现。它们会出现。但是通过仔细关注你的假设、观察到的行为以及你已经收集的证据来证明任何假设,大多数 bug 可以避免。

现在我们已经介绍了程序开发周期,你现在已经准备好编写第一个程序了。随着你阅读本书的进展,你将变得非常熟悉这些周期。

创建、输入和保存你的第一个C程序

让我们开始创建我们的Hello, world!程序。

在开始创建文件之前,在计算机上创建一个目录,用于保存本书中的所有工作。也许你可以在$HOME目录或你的文档文件夹中创建它。我的建议是将它放在你选择的用户目录中的某个位置。现在开始编写我们的程序:

1、打开命令提示符、终端窗口或控制台(取决于你的操作系统)。

2、导航到$HOME或./Documents,或你选择的任何工作位置,并创建一个目录,用于保存本书中编写的程序。使用以下命令进行操作:

$ mkdir PacktLearnC

3、用以下命令将该目录设置为当前工作目录:

$ cd PacktLearnC

4、使用以下命令为本章创建一个新目录:

$ mkdir Chapter1_HelloWorld

5、使用以下命令将该目录设置为当前工作目录:

$ cd Chapter1_HelloWorld

6、选择你喜欢的文本编辑器——任何一个都可以——并打开文本编辑器,无论是从命令行还是GUI(取决于你的操作系统和你希望使用哪个)。

从命令行中,你可以输入$ myEditor hello1.c,或者只输入$ myEditor,稍后,你将需要将文件保存为当前工作目录中的hello1.c。

7、输入以下完全相同的程序文本,同时注意空格、{}、()、""(这些双引号是;和:键旁边的键)与<>,特别注意#、\、.和;:

#include <stdio.h>

int main()

{

printf("Hello, world!\n");

return 0;

}

8、保存你的工作并退出编辑器。

9、通过列出目录并验证其文件大小不为零,验证 hello1.c 是否存在。

恭喜!你已经完成了程序开发周期的第一个编辑阶段。

成功输入并保存了你的 hello1.c 文件后,现在是时候编译它了:

1、在终端、命令行或控制台窗口中(取决于你的操作系统),当前工作目录应该是 hello1.c 文件所在的目录,输入 $ cc hello1.c。

2、完成后,你应该看到一个新的命令行提示符。验证一下你是否有一个名为 a.out 的文件。

你已经完成了程序开发周期的第一个编译阶段。

运行你的第一个C程序

你的 hello1.c 程序已经成功编译,现在你在同一目录下有一个 a.out 文件。是时候运行它了!让我们开始:

1、在终端、命令行或控制台窗口中(取决于你的操作系统),导航到保存了 a.out 的目录。

2、在命令提示符处,通常以第一列中的 > 字符表示,输入 ./a.out。

3、你应该会看到 Hello, world!。

4、如果你看到了这个,我们现在可以验证程序的输出。

5、请注意,命令提示符 $ 不在与 Hello, world! 同一行上。这意味着你在输出流中正确地输入了 \n。如果没有,请重新编辑 hello1.c,并确保 \n 出现在第二个 " 的前面,然后重新编译它并运行 a.out。

6、如果 Hello, world! 单独占一行,并在前后都有命令提示符,那么太棒了!你成功了!

如果一切正常,你应该在终端中看到以下输出:

图 - hello1.c 程序成功编译和执行的结果

在我们的终端会话截图中,> 是终端提示符,表示它准备好接受我们的输入。在第一个提示符后,我们输入了 cc hello1.c,然后按 <return>。接下来看到的是另一个提示符,这意味着编译成功,现在我们可以运行编译后的程序了。然后我们输入 a.out,然后按 <return> 以执行 hello1.c。我们应该会看到我们想要的消息 "Hello, world!",然后等待 Figure 1.1 – 进一步输入。 如果编译器输出任何错误消息,请仔细阅读编译器告诉您的内容,尝试理解它正在告诉您需要修复的错误。

始终专注于第一个错误消息;后面的错误消息通常是第一个错误的结果。然后,回到编辑阶段,检查您输入的程序与本书中所示的程序有何不同。两者必须完全匹配。然后再回到此阶段;希望您的程序能够成功编译(即没有错误消息)。 请记住,当您成功完成某些事情时,请跳一支小舞蹈,感受一下喜悦!编程可能非常令人沮丧,因此记得即使在小小的成功中也要庆祝,这将使您在所有挫折中更加愉快。许多程序员忘记了这个渐进和定期的庆祝步骤! 随着我们在本书中的进展,我们将向 cc 命令添加更多的编译器选项,以使我们的生活更轻松。

提示:如果你跳过了本章的“了解程序开发周期”部分,现在是阅读它并继续学习的好时机。

标签: #复杂c语言程序