1.C预处理器
**C预处理(C Preprocessor)**不是编译器的组成部分,但是它是编译过程中一个单独的步骤。C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。
所有的预处理器命令都是以 “#"开头。
1.1C预定义宏
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
这些预定义符号都是语言内置的
printf("File :%s\n", __FILE__ );
printf("Date :%s\n", __DATE__ );
printf("Time :%s\n", __TIME__ );
printf("Line :%d\n", __LINE__ );
printf("ANSI :%d\n", __STDC__ )
1.2宏定义
使用#define指令可以创建常量宏和函数宏。常量宏会在源代码中的每个出现位置被替换为定义的值,函数宏可以进行更复杂的文本替换。
注意在宏定义时是不需要加上”;“的,
#define pi 3.14
#define SQUARE(x) ((x) * (x))
#define x pi+1
1.3条件编译
使用条件编译指令,如#ifdef、#ifndef、#if、#else、#elif和#endif,可以根据条件编译不同的代码块。
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件
编译指令。
#ifdef DEBUG
// Debugging code 调试代码
#endif
1.4包含头文件
- 本地文件包含
#include"filename"
Linux环境的标准头文件包含
/usr/include
- 库文件包含
#include <filename.h> //预处理先找标准库,再找其他自己命名头文件
#include"filename.h" //自己的头文件一般是这么访问,效率更高
1.5删除注释
预处理器会删除源代码中的注释,以便在编译时不包含注释
#define NAME 15
#undef NAME
//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。
#ifdef DEBUG
// your debugging statements here
#endif
1.6字符串化操作
使用#运算符可以将宏参数转化为字符串常量。
#include <stdio.h>
#define message_for(a, b) \
printf(#a " and " #b ": We like you!\n")
int main(void)
{
message_for(C, D);
return 0;
}
编译结果
C and D :We like you!
1.7标识符连接
使用##运算符可以将两个标识符连接在一起,用于创建新的标识符。
#define CONCAT(x, y) x##y
2.程序环境
在ANSIC的任何一种实现中,存在俩种不同的环境
- 翻译环境:在这个环境中源代码被转换为可执行的机器指令。
- 执行环境:实际执行代码
2.1翻译环境
多个源文件----->编译器 ———>目标文件 (比如:test.i)
多个生成的目标文件-------->链接器<----------链接库
链接器------可执行程序
2.2运行
程序执行的过程:
- 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序
的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。 - 程序的执行便开始。接着便调用main函数。
- 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回
地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程
一直保留他们的值。 - 终止程序。正常终止main函数;也有可能是意外终止。