一、编译链接
1.程序的环境、翻译
- 组成一个程序的每个源文件通过编译转换成目标代码
- 每个目标文件由链接器捆绑在一起,形成一个单一而完整的可执行程序
- 链接器同时也会引入标准C函数库中被该程序调用的函数,也可以搜索程序员个人的程序库,将其需要的函数也链接到程序中
2.程序执行过程
- 程序必须载入内存。有操作系统时,一般这个由操作系统完成;独立环境下,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成的
- 程序执行便开始。接着调用main函数
- 开始执行程序代码。程序将使用一个运行时堆栈,存储函数的局部变量和返回地址。程序同时也可以使用静态内存,存储于静态内存中的变量在程序的整个执行过程一直保留它们的值
- 终止程序。正常终止main函数,或者是意外终止
3.程序编译链接过程
- 预处理:头文件展开、宏替换、条件编译、去掉注释
- 编译:语法检查、生成汇编代码
- 汇编:将汇编代码转换成二进制码
- 链接:生成可执行程序
二、宏
允许把参数替换到文本中,这种实现通常称为宏或定义宏。
1.#define 定义标识符
例如:#define name stuff
如果是多行替换的话,必须每行后面加上续行符 ’ \ ’ ,因为宏只会替换后面的一行而已。
例如:
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
date:%s\ttime:%s\n" ,\
__FILE__,__LINE__ , \
__DATE__,__TIME__ )
注意:在定义宏时,应尽可能地多使用括号,避免临近操作符之间的相互作用。
2.宏替换
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被它们的值替换。
- 最后,再次对结果文件进行扫描,重复1号过程。
注意:宏参数和#define定义中可以出现其他#define定义的变量,宏不能出现递归。当处理器搜索#define定义的符号时,字符串常量不被搜索。
3.#和##
#:把一个宏参数变成对应的字符串。
例如:
##:可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本片段创建标识符。
例如:
#define ADD_TO_SUM(num,value)\
sum##num+=value;
ADD_TO_SUM(3, 7);// 给sum3增加7
4.宏和函数
宏的优势:
- 宏比函数在程序的规模和速度方面更胜一筹。
- 宏与类型无关。
宏的劣势:
- 长的宏会增加代码篇幅。
- 宏没法调试。
- 宏与类型无关,不够严谨。
- 宏会有优先级问题。
三、条件编译
常用:
#if #endif
#if #elif #else #endif
条件编译可以跨平台。
四、头文件展开
-
#include指令使另外一个文件被编译:预处理器先删除这条指令,并用包含文件的内容替换。这样一个文件被包含10次,那就实际被编译10次。
-
库文件一般用 < > 包含;本地文件一般用 “ ” 包含。
-
文件开头写:#pragma once 可以避免头文件的重复引入。