1. 宏定义
C语言程序的编译过程主要由预处理、编译、汇编以及链接四个步骤组成。其中,预处理就是将预处理指令转换为实际代码中的内容,也就是把程序中“#”后面的内容进行替换。如果想要查看具体内容,则可以在命令行中输入以下指令:
gcc -E main.c
部分运行结果:
“#”后面的内容主要有以下两种:
1)#include - 包含头文件
此操作会将所包含的头文件的内容进行复制,从而使程序可以调用这些头文件中定义的变量或函数。如果该头文件中包含了其他的头文件,也需要将这些头文件展开包含。
2)#define - 宏定义
- 宏定义是将数字、字符串、函数进行替换,方便使用。
- 宏定义只会进行简单的替换,而不会计算优先级的问题。如下列代码所示,定义的内容只是将A+B直接进行替换,在实际使用的时候要注意加括号保证运算的优先级。
#define SUM(A, B) A + B
// SUM(10,100) * 6 = 10 + 100 * 6 == 610!!
printf("%d\n", SUM(10, 100)); // 10 + 100 = 110
printf("%d\n", SUM(10, 100) * 6); // 10 + 100 * 6 = 610
printf("%d\n", (SUM(10, 100)) * 6); // (10 + 100) * 6 = 660
- 与全局变量相比,宏定义会不会占用内存。
2. 条件处理命令和其他命令
1)宏定义相关操作
- #define - 宏定义
- #undef - 取消定义的宏
- #ifdef - if defined,如果已经定义,则……
- #ifndef - if not defined,如果未定义,则……
2)条件编译 - 使用方法与if语句基本相同,唯一的区别在于必须以#endif结束
- #if - 条件起始,必须加上判断条件(真/假)
- #elif - 如果前一个条件不成立,进入elif进行处理;反之则不会进入
- #else - 以上两个条件均不成立时进入
- #endif - 结束条件编译
3)其他指令
- #warning - 让编译器发出错误警告信息,可以用来表示代码仍需处理
- #error - 造成编译错误,代码无法编译通过
- 对于#warning和#error可以直接加上警告和错误信息,不需要加双引号
- #pragma - 设定编译器的状态或者是指示编译器完成一些特定的动作
- #pragma warning(“消息文本”)
非致命性警告,只有编译时添加了 -Wall 才会提示,-Wall表示显示所有警告(W:警告,all:所有) - #pragma message(“消息文本”)
直接在编译信息输出窗口中输出相应的信息,当程序中定义了许多宏来的时候,有时会忘记是否正确的设置这些宏,这条指令可以在编译的时候检查提示是否有定义宏 - #pragma pack(n)
- 内存对齐方式,括号内指定内存对齐的字节数
- #pragma warning(“消息文本”)
3. 头文件
为了避免主程序中代码过于冗杂,我们常常会将一些函数原型、类型、变量等定义在头文件中,然后再创建一个名称相同的.c文件进行函数的实现,因为如果将函数的实现放在头文件内会导致编译效率降低。头文件定义的格式通常如下:
#ifndef __HEADER_H
#define __HEADER_H
#endif /* __HEADER_H*/
头文件的引用方式:
-
<header.h>
直接从系统当中的路径查找对应的头文件,如果引用自定义的头文件,会在编译阶段出错 -
“header.h”
- 先从当前源文件路径查找。如果查找不到,再到系统路径当中查找
- 如果文件与源文件不在同一目录,需要引用相对路径下的头文件
4. 多文件编译
对于一个完整的项目而言,进行模块化管理是很有必要的。需要把不同的文件放到相应的路径下,便于管理。
如果在主程序中包含了自定义的头文件,命令行中编译代码的指令也需要链接相应的目标文件,并在指定的路径下生成可执行文件,例如:
gcc main.c source/test.c -o output/result