C编程
“Hello, World!” 程序
传统上,我们从一个在屏幕上显示“Hello, World!”问候语的程序开始,接着是换行,然后退出。下面是实现这一功能的 C 源代码。将此代码输入到你喜欢的文本编辑器/IDE 中,并将其保存为 hello.c
文件。
#include <stdio.h>
int main(void)
{
printf("Hello, World!\n");
return 0;
}
源代码分析
虽然这是一个非常简单的程序,但你会发现代码中的许多符号蕴含着深刻的含义。虽然编译器能理解这些符号,但对于你来说,这些符号和一些熟悉的英语单词所代表的具体功能可能难以理解。作为新手程序员,你的第一项任务就是学习 C 语言中的各种“单词”和符号——即编译器能理解的语言。一旦你理解了这些代码背后的含义,你就能够“与编译器对话”,指示它执行你的命令,从而构建任何你富有创意的程序。
但是需要注意的是,了解这些晦涩符号的含义并不是编程的全部。你不能通过阅读翻译词典就掌握一门语言。要精通一门语言,你必须不断地用它进行实际对话。学习编程语言也是一样。你必须通过自己编写源代码,与编译器进行“对话”。因此,务必按照上述代码示例进行练习,并且可以根据自己的好奇心进行修改和实验。
现在,让我们深入看看程序中的第一行代码:
#include <stdio.h>
在理解这一行代码的作用之前,你需要知道你的计算机已经预先安装了一些 C 语言的软件代码。这些代码可以帮助你省去编写一些常见、基础任务代码的麻烦。这些可重复使用的代码被称为库。因此,在我们示例程序的第一行,我们告知编译器,我们希望从库中“借用”一些代码,并在程序中使用它。这里,我们借用了能够帮助我们将文本打印到屏幕上的代码。
告诉 C 编译器如何将库代码包含到我们的代码中的方法是使用预处理指令。编译器执行的第一步就是在你的源代码中查找预处理指令,这些指令会以某种方式修改源代码。在我们的例子中,#include
预处理指令告诉编译器从库中复制源代码并将其插入到代码中的预处理指令所在的位置。由于我们的指令位于文件的顶部,因此库代码会被插入到源文件的顶部。(请注意,这一切都发生在计算机的内存中,因此原始文件本身并不会被修改。)
那么,编译器应该插入哪个库代码呢?接下来的 <stdio.h>
部分告诉编译器从 stdio.h
文件中复制和粘贴 C 代码到我们的代码中。文件名周围的尖括号告诉编译器从标准库中查找该文件,而不是从你个人的可重用代码库中查找。请注意,.h
扩展名的文件被称为头文件。stdio.h
头文件包含了许多与输入和输出相关的函数,这些函数是按照 C 标准定义的。虽然这个头文件提供了许多不同的函数,但我们这里只关心其中一个:printf
函数。
接下来,我们来看一下代码中的下一行:
int main(void)
在这一行代码中,我们创建了一个名为 main
的函数,它是所有 C 程序的起始点。所有的 C 程序都要求必须有一个名为 main
的函数,否则程序将无法编译。我们的函数名被两个神秘符号 int
和 (void)
所包围。int
部分告诉编译器我们的函数将返回什么类型的值,而 (void)
部分则告诉编译器我们将向函数“传递”什么类型的值。我们暂时跳过这些含义,因为这些内容会在本书的后面部分详细讲解。现在最重要的是理解,组合起来,这些符号向编译器声明了我们的函数,并告诉它该函数存在。
那么,什么是函数呢?在计算机科学中,“函数”这个术语的使用比数学中要宽松一些,因为函数通常表示命令式的思想(例如 C 语言中的函数)——即处理过程,而不是声明。现在,简单来说,函数定义了一组计算机语句,这些语句协同工作以完成特定的任务。在 C 语言中,函数相关的语句被放置在一对大括号 {}
之间,这标志着语句的开始和结束。大括号和语句组合在一起,称为“块”。接下来,让我们看一看函数块中的第一行代码:
printf("Hello World!\n");
这一行代码是我们程序的核心,它负责向用户的控制台(在类 Unix 操作系统中也叫做终端)输出我们的问候语。该语句是一个函数调用,包含两个主要部分:用于打印问候语的库函数名称 printf
,以及我们将传递给函数的数据,即括号中的数据。我们传递给函数的数据是字符串 “Hello
World!\n”
。字符串结尾的 \n
是一种特殊字符,称为转义序列。 \n
转义序列表示换行。字符串和转义序列将在后续章节中详细讲解。我们用分号结束这个函数调用语句,以便编译器知道接下来应该寻找新的语句,接下来我们看到的语句是:
return 0;
在这一行中,我们通过 return
关键字告诉编译器我们的 main
函数将返回一个整数值。我们返回的整数值是 “0”。但是,这到底是什么意思呢?在 main
函数的特定上下文中,我们返回的值称为退出状态,它报告给操作系统,表明我们的代码是否没有错误地运行。当我们的程序变得更加复杂时,我们可以使用其他整数代码来指示不同类型的错误。提供退出状态的这种做法已经成为一种长期以来的约定[1]。我们将在本书的后面部分详细讨论函数返回值。
所以,对于这么短小的程序来说,要消化这些内容还是有点难度的。不要担心你是否能理解所有的内容,也不用担心要记住它们。编程不是通过死记硬背来掌握的,而是通过重复和实践来学习的。记住所有贝多芬第五交响曲的音符并不能让你成为一位钢琴家,你必须坐到钢琴前练习和演奏!
接下来,我们将展示如何将你输入的源代码通过编译器转换成可执行文件。
编译
编译是将你在源代码中给编译器下达的指令翻译成机器语言的过程,这样操作系统和微处理器就能运行它。换句话说,C 编译器充当了中介。你使用编译器理解的语言——C 源代码,编译器则将其翻译成机器代码,从而节省了你编写汇编代码的繁琐工作。
如果编译器发现你的源代码有问题,它会抛出一个错误,并给出一条帮助信息,以帮助你修正代码中的问题。然后,你需要重新编译代码,并重复这一过程,直到代码能够无错误地编译。需要注意的是,代码成功编译并不意味着没有错误,它只是表示编译器能够理解源代码中的指令。
类 Unix 系统
如果你使用的是类 Unix 系统,如 GNU/Linux、Mac OS X 或 Solaris,可能已经安装了 GCC。如果没有,在 Linux 上你可以通过发行版的包管理器来安装它。打开虚拟控制台或终端模拟器,输入以下命令(确保当前工作目录是包含源代码的目录):
gcc hello.c
默认情况下,GCC 会生成名为 a.out
的可执行文件。要运行新生成的程序,输入以下命令:
./a.out
你应该能看到 “Hello, World!” 被打印在屏幕上。
要查看最后一个程序的退出状态,可以在终端输入:
echo $?
这会显示 main
函数返回的值,在上面的例子中是 0
。
GCC 的常用选项
GCC 编译器有很多选项。例如,如果你希望输出文件的名称不是 a.out
,可以使用 -o
选项。以下是一些示例:
-o
表示接下来的参数是生成的程序(或库)的名称。如果没有指定此选项,编译后的程序将被默认命名为 a.out
或 a.exe
(对于 Cygwin 用户)。
-Wall
表示 GCC 应该警告许多类型的可疑代码,这些代码可能是错误的。
你可以使用这些选项来创建一个名为
helloworld
的程序,而不是默认的 a.out
,命令如下:
gcc -o helloworld hello.c -Wall
然后可以通过以下命令运行:
./helloworld
GCC 的所有选项都有详细的文档说明[2]。
在 IDE 中
如果你使用的是 IDE,你可能需要选择控制台项目,并通过菜单或工具栏中的“构建”选项来编译程序。编译后的可执行文件将出现在项目文件夹中,但你应该有一个菜单按钮,可以直接从 IDE 中运行可执行文件。所有 IDE 中的过程大致相同。