第二章:基本概念
《C和指针》这本书阅读前提是有一定的C语言基础,不建议萌新入坑
2.1 环境
ANSI C(C语言的标准)在任何一种实现中,有两种不同的环境:翻译环境和执行环境。
翻译环境:源代码被转换成可执行的机器指令。
执行环境:用于实际执行代码。
这两种环境不必在同一台机器上,eg.交叉编译环境
2.1.1 翻译
翻译过程具体描述为,将一个或多个源文件经过 编译器(Compiler) 编译生成目标代码·obj或.o
,产生的一个或多个目标代码通过 链接器(Linker) 链接捆绑,形成一个单一而完整的可执行程序.exe
。对于libraries是指在链接的同时引入标准C函数库或个人程序库中任何被该程序所用到的函数。
编译过程也由几个阶段组成,主要由预处理器(preprocessor)处理 和 解析(parse) 两个阶段组成。预处理器处理:在源代码上执行文本操作,eg.实际值代替由#define
指令定义的符号,#include
指令包含的文件内容以及将注释部分替换成空格。解析:判断语句的意思,绝大多数错误和警告产生的地方。
一、文件名约定
源代码:xxx.c
头文件:xxx.h
目标文件:UNIX系统中xxx.o
,MS-DOS系统中xxx.obj
可执行文件:UNIX系统中xxx.out
,MS-DOS系统中xxx.exe
二、编译和链接
编译和链接C程序的特定命令在不同的系统中各不相同,以UNIX系统举例子,C编译器被称为cc
编译+链接
.c
文件cc main.c #编译+链接一个.c cc main.c test01.c test02.c #编译+链接多个.c
编译
.c
+链接现存.o
文件cc main.o test01.o test02.c
为什么说是现存的
.o
文件呢?这里解释一下,在执行完cc main.c
后,会产生一个称为a.out
的可执行文件,而这中间会产生program.o
的目标文件,目标文件在链接完毕后会自动删除。但是在执行完cc main.c test01.c test02.c
(编译多个时),目标文件不会被删除,这就允许对程序进行修改后,只对改动过的源文件进行重新编译。只编译
cc -c main.c #编译一个.c cc -c main.c test01.c test02.c #编译多个.c
只链接
cc main.o test01.o test02.o
- 还有一些选项配置自行查询linux资料即可
- UNIX系统和MS-DOS系统的区别
UNIX | MS-DOS | |
---|---|---|
目标文件 | xxx.o |
xxx.obj |
当单个.c文件被编译和链接 | 删除目标文件 | 不删除目标文件 |
当多个.c文件被编译和链接 | 不删除目标文件 | 不删除目标文件 |
2.1.2 执行
执行过程也需要几个阶段:首先程序载入到内存,然后执行程序。
step1: 首先程序载入到内存,这个任务由操作系统完成,那些不是存储在堆栈中的尚未初始化的变量将在这个时候得到初始值。
在C语言中,变量根据其 存储类别(storage class) 和 生命周期(lifetime) 被存储在不同的内存区域中。这些区域包括堆栈(stack)、堆(heap)、 全局/静态存储区(global/static storage area) 等。当一个变量不是存储在堆栈上时,它通常是在全局/静态存储区或堆中。
对于全局变量和静态变量(包括在函数内部定义的静态变量),它们在程序开始执行之前就已经在全局/静态存储区分配了内存空间。这些变量在声明时如果没有显式地初始化,编译器会自动将它们初始化为默认值。对于基本数据类型(如int、char等),这个默认值通常是0;对于指针类型,默认值通常是NULL。
举个例子:
#include <stdio.h>
int globalVar; // 全局变量,未显式初始化,但会被自动初始化为0
int main() {
static int staticVar; // 静态局部变量,未显式初始化,但会被自动初始化为0
int stackVar; // 局部变量,存储在堆栈上,未初始化则不会得到自动初始值
printf("globalVar = %d\n", globalVar); // 输出0,因为全局变量被自动初始化了
printf("staticVar = %d\n", staticVar); // 输出0,因为静态局部变量也被自动初始化了
// printf("stackVar = %d\n", stackVar); // 如果取消注释,这里会得到一个未定义的值,因为stackVar未初始化
return 0;
}
在这个例子中,globalVar
和 staticVar
都没有显式初始化。但是,由于它们不是存储在堆栈上的变量,编译器会自动将它们初始化为0。而 stackVar
是一个局部变量,存储在堆栈上,如果没有显式初始化,则它不会得到自动初始值,其内容将是未定义的。
step2: 执行程序代码。首先由一个小型启动程序与程序链接在一起,接着调用main
函数执行程序代码,最后程序终止。
2.2 词法规则
词法规则就像英语中的拼写规则,决定你在源程序中如何形成单独的字符片段,也就是标记(token)
2.2.1 字符
三字母词:三个字符的序列,合起来表示另一个字符。在以前的老式键盘中,类似“ [ ] { } ^| "等符号是没有的,当时为了解决这个问题,C语言中出现了所谓的“三字母词”现在很多编译器都不需要这个转义功能了,因为现在的键盘可以直接敲出这些字符。所以了解即可。
转义字符
2.2.2 注释
注释以/*
开始*/
结束,中间可以包含除了*/
之外的任何字符。这意味着,注释不能嵌套,若嵌套将会出现/* /*...*/ */
第一个/*
匹配了倒数第二个*/
所有的注释都会在预处理阶段被替换成空格。也就是说,注释可以出现在任何空格可以出现的地方
2.2.3 自由形式的源代码
C是一种自由形式的语言,也就是说并没有规则规定什么地方可以书写语句,一行中可以出现多少条语句,什么地方应该留下空白以及应该出现多少空白等,唯一的规则就是相邻的标记之间必须出现一至多个空白字符(或注释),不然它们可能被解释为单个标记。举个例子
int x;//√
int
x;//√
int/*...*/x;//√
intx;//×
2.2.4 标识符
标识符(identifier) 就是变量、函数、类型等的名字。它们由大小写字母、数字和下划线组成,不能以数字开头。
2.2.5 程序的形式
一个C程序可能保存于一个或多个源文件中。虽然一个源文件可以包含超过一个的函数,但每个函数都必须完整地出现于同一个源文件中。