Beginning C , Fifth Edition 第1章: C 语言编程
程序即一组指令,可以让计算机依指令行事,完成指定的任务。
本章的主要内容
- C 语言标准
- 标准库的概念
- 如何创建 C 程序
- 如何组织 C 程序
- 如何编写在屏幕上显示文本的程序
标准库定义了编写C程序时常常需要的常量、符号和函数。它还提供了基本C语言的一些可选扩展,标准库以不依赖机器的形式实现,即相同的C代码在不同的底层硬件上会实现相同的功能。
标准库在一系列文件——头文件中指定。头文件的扩展名总是.h。使用一组标准功能可用于C程序文件,只需要将对应的标准头文件含进来。
C 程序的创建工程有 4 个基本步骤:
- 编辑
- 编译
- 链接
- 执行
程序要运行起来,必须要经过四个步骤:预处理、编译、汇编和链接。接下来通过几个简单的例子来详细讲解一下这些过程。
对于上边用到的几个选项需要说明一下。
使用 gcc 命令不跟任何的选项的话,会默认执行预处理、编译、汇编、链接这整个过程,如果程序没有错,就会得到一个可执行文件,默认为a.out
-E选项:提示编译器执行完预处理就停下来,后边的编译、汇编、链接就先不执行了。
-S选项:提示编译器执行完编译就停下来,不去执行汇编和链接了。
-c选项:提示编译器执行完汇编就停下来。
所以,这三个选项相当于是限定了编译器执行操作的停止时间,而不是单独的将某一步拎出来执行。
创建和修改 C 程序的源代码——我们编写的程序指令称之为源代码。
编译器可以将源代码转换成机器语言,在编译过程中,会找到并报告错误。这个阶段的输入是在编译期间产生的包含源代码的文件,常称为源文件。
编译器会检查源文件中的错误。
编译器输出的结果成为对象代码,存放于对象文件它与源文件同名,这些文件在Windows中扩展名通常是 .obj,Linux中通常是.o
若编译器是GUN,编译命令是:
gcc -c main.c
如果省略了 -c 这个参数,程序还会自动链接,生成可执行文件。
编译过程包括两个阶段:第一个阶段称为预处理阶段,在此期间会修改或添加代码。第二阶段是生成对象代码的实际编译过程。源文件可以包含预处理宏,他们用于添加或修改 C 语言程序。
链接器(linker)将源代码文件中有编译器产生的各种对象模块组合起来,再从 C 语言提供的程序库中添加必要的代码模块,将他们组合成一个可执行的文件。
链接器也会检测和报告错误,
可以将一个程序拆分成几个源代码文件,再用链接器连接起来,每个源文件提供部分功能,每个源文件分别编译。
链接阶段出现错误,意味着要重新编译源代码,链接成功则会产生一个可执行文件,但这并不意味着程序能正常工作。
执行阶段就是当成功完成了前述 3 个过程后运行程序。
#include <stdio.h>
int main(void){
printf("Hello world!");
return 0;
}
/* 注意 \" , 在要显示的文本中,\"序列称之为转义序列(escape sequence)。 */
#include <stdio.h>
int main(void){
printf("Hello world!\n");
return 0;
}
在要显示的文本中," 序列称为转义序列,
反斜杠()在文本字符串里具有特殊的意义,他标识转义序列的开始。反斜杆后面的字符表示是哪种转义序列。
表 1-1 是转义序列表:
根据编译器提示,处理错误吧。
多行注释:/* */;无论 /* 之后的任何文本都会被认为是注释直到标示注释结束的 */ 为止。
单行注释://;代码行上两个斜杠后面的所有内容都会被编译器忽略。
下面的代码行:
#include <stdio.h>
符号 # 表示这是一个预处理指令(preprocessing directive),告诉编译器在编译源代码前,要先执行一些操作,编译器在编译过程开始前的预处理阶段处理这些指令,预处理指令相当多,大多数放于程序源文件的开头。
在这个例子中,编译器要将 stdio.h 文件的内容包含进来,这个文件称之为头文件(header file),因为它通常放在程序的开头出。在本例中,头文件定义了 C 标准库中的一些函数信息,通常,在头文件中指定的信息应有编译器用于在程序中集成预定义函数和其他全局对象,有时需要创建自己的头文件。本例中用到标准库中的printf()函数,所以必须包含 stdio.h 头文件。 stdio.h 头文件包含了编译器理解 printf() 以及其他输入/输出函数所需要的信息。名称 stdio 是标准输入/输出(standard input/output)的缩写。C 语言中所有的头文件的扩展名都是 .h 。
每个符合 C11 标准的 C 编译器 都有一些标准的头文件。这些头文件主要包含了与 C 标准库函数相关的声明。所有符合该标准的 C 编译器都支持同一组标准库函数,有同一组标准头文件,有一些编译器有额外的库函数,他们提供的功能一般是运行编译器的计算机所专用的。
注意:
在一些系统中,头文件名是不区分大小写的,但在#include 指令里,这些文件名通常是小写。
下面的 5 行指令定义了main()函数:
int main(void)
{
printf("Hello world!\n");
return 0;
}
函数是两个括号主键执行某组操作的一段代码,每个 C 程序都是由一个或多个函数组成,每个 C 程序都必须有个 main() 函数,main()函数是每个 C 程序的执行起点。在执行阶段执行可执行文件时,操作系统会执行这个程序的main()函数。
定义main()函数的第一行代码如下:
int main(void)
定义 main() 函数的第一行代码开头是一个关键字 int,它标示 main() 函数的返回值类型。
在下面的语句中,指定了执行完 main()函数后要返回的值:
return 0;
这个 return 语句结束了 main() 函数的执行。
函数名“main”后面的括号,是给函数main传递信息的入口,void 表示 给 main()函数传递的数据是无。
在执行到函数体中的 return 语句时,就会停止执行该函数,将控制权返回给调用函数(对于 main 函数,则将控制权返回给操作系统)。
函数体是在函数名称后面起始及结束两个大括号 之间 的 代码块。它们定义了函数执行时要完成的任务。
每个函数必须有函数体,但函数体可以是空的。
printf() 是一个标准的库函数,它将函数名后面引号内的信息输出到命令行上(实际上是标准输出流,默认为命令行)。在这个例子中,调用这个函数会显示双引号内的一段警示语:双引号内的字符串称为字符串字面量。注意这行代码用分号作为结尾。
包含在函数名后圆括号内的项称为参数,它是指要传递诶函数的数据,当一个函数有多个参数时,要用逗号隔开。
注意:
与C语言中所有可执行的语句一样, printf()行的末尾必须有分号(这与定义语句或指令语句不同),这是一个很容易犯的错误,尤其是初次使用C编程的人,老是忘了分号。
反斜杠()在文本字符串里有特殊的意义,它表示转义序列的开始。反斜杠后面的字符表示是哪种转义序列。对于\n, n表示换行。还有其他许多转义序列。显然,反斜杠是有特殊意义的,所以需要一种方式在字符串中指定反斜杠。为此,应使用两个反斜杠(\)。
这类转义序列存在的唯一原因是,有9个特殊的字母序列,称之为三字母序列,这是包含三个字母的序列,分别表示#、[、]、\、^、~、|、{和}:
在 International Organization of Standardization(ISO)不变的代码集中编写C代码时,需要用到它们,可以完全不用理会它们,使用三字母序列时,编译器会发出一个警告,因为通常是不应该使用三字母序列的。
预处理指令可以把头文件的内容包含到源文件中。编译的预处理阶段可以做的工作远不止此,除了指令外,源文件还可以包含宏。宏是提供给预处理器的指令,来添加或修改程序中的 C 语句。宏可以很简单,只定义一个符号,例如 INCHES_PER_FOOT ,只要出现这个符号,就用 12 代替。其指令如下:
#define INCHES_PER_FOOT 12
在源文件中包含这个指令,则代码中只要出现 INCHES_PER_FOOT ,就用 12 替代它。例如:
printf("Hello world!\n",INCHES_PER_FOOT);
在预处理后,这个语句变成:
printf("Hello world!\n",12);
宏也可以很复杂,根据特定的条件把大量代码添加到源文件中。
介绍编写程序时需要完成的基本步骤。
在满足需求和完成项目的有限资金、人力及时间之间达成某种一致。
了解需求,定义程序要解决的问题部分。
讲需求分解为可管理的单元模块,描述这些独立模块互相沟通的方式。
有计划的实施。
大多数编程语言都提供一种方法,将程序切割成多个段,各个段都可以独立编写。在 C 语言中这些段称之为函数。
函数与外界有一个特殊的接口,可以将信息传进来,也可以减函数产生的结果传出去。
每个函数都完成一个指定的、定义明确的工作,程序中操作的执行由一个main()总体掌控。
程序切割成多个易于管理的小单元,对编程非常重要。
编译和连接报错好找,但是不报错缺得不到正确的结果才是头疼的。