C语言深度解剖 -- 预处理

1、LINE 表示正在编译的文件的行号,
2、FILE 表示正在编译的文件的名字,
3、DATE 表示编译时刻的日期字符串,
4、TIME 表示编译时刻的时间字符串,
5、STDC 判断该文件是不是定义成标准C程序。

一、宏定义

1、数值宏常量

#define PI 3.141592654
#define ERROR_POWEROFF  -1

2、字符串宏常量

#define ENG_PATH_1  E:\English\listen_to_this\listen_to_this_3

3、用define 宏定义注释符号“ ? ”

#define BSC //
#define BMC /*
#define EMC */
(D) BSC my single-line comment
(E) BMC my multi-line comment EMC

(D) 和 (E) 都是错误的 ,原因:注释先于预处理指令被处理,当这两行被展开成“ // … ” 或 “/ * … * /” 时,注释已经处理完毕,此时再出现“ // … ” 或 “/ * … * /”自然错误。因此,试图用宏开始或结束一段注释是不行的
4、用define 宏定义表达式
(1)使用宏定义表达式时,不要吝啬括号。
(2)宏函数被调用时,是以实参代换形参,而不是 “ 值传送 ”
5、宏定义中的空格

#define SUM (x) (x) + (x)
定义了一个宏:SUM 其代表的是(x) (x) + (x)
空格仅在定义的时候有效,在使用这个宏函数的时候 ,空格会被编译器忽略。

6、#undef
(1)作用:用来撤销宏定义
(2)宏的生命周期从#define开始到 #undef 结束

二、条件编译

1、条件编译的功能:使得我们可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。
2、条件编译的三种形式:

①   #ifdef 标识符
	程序段1
	#else
	程序段2
	#endif
	
②	#ifndef 标识符
	程序段1
	#else
	程序段2
	#endif
	
③  #if 常量表达式
   程序段1
   #else
   程序段2
   #endif

三、文件包含

1、文件包含是预处理的一个重要功能,它将多个源文件链接成一个源文件进行编译,结果生成一个目标文件。
2.文件包含的两种形式:

格式一:
#include 
亦称头文件
表示预处理要到系统规定的路径中去获得这个文件(即C编译系统提供的
并存放在指定子目录下的头文件),找到文件后,用文件内容替换该语句。
格式二:
#include "filename"
表示预处理应在当前目录中查找文件名为filename的文件;若没有找到,
则按系统指定的路径信息搜素其他目录。找到文件后,用文件内容替换该语句

注意:#include 是将已存在文件的内容,嵌入到当前的文件中。
#include 支持相对路径,. 代表当前目录 , ..代表上层目录

四、#error预处理

1、作用:编译程序时,只要遇到 #error 就会生成一个编译错误提示错误,并停止编译。

#error error_message

五、#line预处理

1、作用:改变当前行数和文件名称,它们是在编译程序中预先定义的标识符。

#line number[ "filename" ]		[ ]内的文件名可省略
eg:#line 30 a.h     		    改变当前的行号为30,文件名为 a.h

2、我们知道编译器对C源码编译的过程中会产生一些中间文件,通过这条指令,可以保证文件名是固定的,不会被这些中间文件代替,有利于分析。

六、#pragma预处理

1、作用:设定编译器的状态或者是指示编译器完成一些特定的动作。
2、格式:

#pragma  para 
para 为参数

3、#pragma message

#pragma message("消息文本")   当编译器遇到这条指令,就会在
编译 窗口中将消息文本打印出来
#ifdef_X86
#pragma message("_X86 macro activated!")
#endif
检查自己是否定义了 _X86 这个宏,如果定义了,编译窗口就会输出
_X86 macro activated!

4、#pragma code_seg

#pragma code_seg(["section - name"[,  "section - class"]])
他能够设置程序中函数代码存放的代码段,当我们开发程序驱动时就会使用到他。

5、#pragma once
只要在头文件的最开始加入这条指令 就能够保证头文件被编译一次。

6、#pragma hdrstop
表示 预编译头文件到此为止,后面的头文件不进行预编译。
可以用 #pragma startup指定编译优先级,如果使用了 #pragma package(smart_init) ,BCB 就会根据优先级的大小先后编译。

7、#pragma resource

#pragma resource " *.dfm "		表示把 *.dfm 文件中的资源加入工程。
*.dfm 中包括窗体外观的定义

8、#pragma warning

#pragma warning(disable: 4507 34; once: 4385; error: 164)
含义:不显示 4507 和34 号警告信息
4385号警告信息仅报告一次
把164号警告信息作为一个错误

9、#pragma comment

#pragma comment( ... )
该指令将一个注释记录放入一个对象文件或可执行文件中

10、#pragma pack
(1) 内存对齐

struct TestStruct1
{
	char c1;
	short  s;
	char c2;
	int i;
};

sizeof(TestStruct1) = 12

(2)如何避免内存对齐的影响

struct TestStruct2
{
	char c1;
	char c2;
	short s;
	int i;
};
sizeof(TestStruct2) = 8
#pragma  pack(n)	//编译器将按照n字节对齐
#pragma  pack()     //编译器将取消自定义字节对齐方式

(3)对齐的规则:每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是n字节)中较小的一个对齐,即min(n, sizeof(item)),并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节。

注意:
①:每个成员分别按照自己的方式对齐,并能最小化长度
②:复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在
成员是复杂类型时,可以最小化长度
③:对齐后的长度必须是成员中最大的对齐参数的整数倍。这样在处理数组时
可以保证每一项都边界对齐。
char a[3]   对齐方式:按1字节对齐

七、“#”运算符

1、字符串中包含宏参数,可以使用“#”,可以把语言符号转化为字符串。

#define SQR(x)  printf("The square of  " #x " is %d. \n", ((x) * (x)));
SQR(8)

输出:The square of 8 is 64.

八、“##”运算符

1、也可以用于宏函数的替换部分,这个运算符把两个语言符号组合成单个语言符号

#define XNAME(n)  x ## n
XNAME(8)

被展开为:x8
“ ## ” 就是个粘合剂,将前后两部分粘合起来。

你可能感兴趣的:(C语言深度解剖 -- 预处理)