天天看点

C-程序的编译链接、宏、条件编译、头文件展开

文章目录

    • 一、编译链接
    • 二、宏
    • 三、条件编译
    • 四、头文件展开

一、编译链接

1.程序的环境、翻译

  1. 组成一个程序的每个源文件通过编译转换成目标代码
  2. 每个目标文件由链接器捆绑在一起,形成一个单一而完整的可执行程序
  3. 链接器同时也会引入标准C函数库中被该程序调用的函数,也可以搜索程序员个人的程序库,将其需要的函数也链接到程序中

2.程序执行过程

  1. 程序必须载入内存。有操作系统时,一般这个由操作系统完成;独立环境下,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成的
  2. 程序执行便开始。接着调用main函数
  3. 开始执行程序代码。程序将使用一个运行时堆栈,存储函数的局部变量和返回地址。程序同时也可以使用静态内存,存储于静态内存中的变量在程序的整个执行过程一直保留它们的值
  4. 终止程序。正常终止main函数,或者是意外终止

3.程序编译链接过程

  1. 预处理:头文件展开、宏替换、条件编译、去掉注释
  2. 编译:语法检查、生成汇编代码
  3. 汇编:将汇编代码转换成二进制码
  4. 链接:生成可执行程序
    C-程序的编译链接、宏、条件编译、头文件展开

二、宏

允许把参数替换到文本中,这种实现通常称为宏或定义宏。

1.#define 定义标识符

例如:#define name stuff

如果是多行替换的话,必须每行后面加上续行符 ’ \ ’ ,因为宏只会替换后面的一行而已。

例如:

#define DEBUG_PRINT printf("file:%s\tline:%d\t  \
                           date:%s\ttime:%s\n" ,\
                           __FILE__,__LINE__ ,  \
	                       __DATE__,__TIME__ )
           

注意:在定义宏时,应尽可能地多使用括号,避免临近操作符之间的相互作用。

2.宏替换

  1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被它们的值替换。
  3. 最后,再次对结果文件进行扫描,重复1号过程。

注意:宏参数和#define定义中可以出现其他#define定义的变量,宏不能出现递归。当处理器搜索#define定义的符号时,字符串常量不被搜索。

3.#和##

#:把一个宏参数变成对应的字符串。

例如:

C-程序的编译链接、宏、条件编译、头文件展开

##:可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本片段创建标识符。

例如:

#define ADD_TO_SUM(num,value)\
sum##num+=value;
ADD_TO_SUM(3, 7);// 给sum3增加7
           

4.宏和函数

宏的优势:

  • 宏比函数在程序的规模和速度方面更胜一筹。
  • 宏与类型无关。

宏的劣势:

  • 长的宏会增加代码篇幅。
  • 宏没法调试。
  • 宏与类型无关,不够严谨。
  • 宏会有优先级问题。

三、条件编译

常用:

  1. #if #endif
  2. #if #elif #else #endif

条件编译可以跨平台。

四、头文件展开

  1. #include指令使另外一个文件被编译:预处理器先删除这条指令,并用包含文件的内容替换。这样一个文件被包含10次,那就实际被编译10次。
  2. 库文件一般用 < > 包含;本地文件一般用 “ ” 包含。
  3. 文件开头写:#pragma once 可以避免头文件的重复引入。

继续阅读