所谓预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理是C语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。
1.1 define宏定义
C语言提供了多种预处理功能,如宏定义、文件包含、条件编译等。
在C语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。
宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。在C语言中,“宏”分为有参数和无参数两种。
C语言对宏定义用#define命令来实现,注意宏定义后字符串需要用圆括号括起来,防止宏代换时产生歧义和隐含错误。
1. 无参宏定义
无参宏定义的一般形式如下:
#define 标识符 字符串
如:#define M (y*y+3*y),可用#undef M解除定义。
2. 有参宏定义
有参宏定义的一般形式如下:
#define 宏名(形参表) 字符串
上述宏定义在字符串中含有各个形参。
有参宏调用的一般形式如下:
宏名(实参表);
例如:
#define M(y) y*y+3*y /*宏定义*/
……
k=M(5); /*宏调用*/
……
在宏调用时,用实参5去代替形参y,经预处理宏展开后的语句为:
k=5*5+3*5
1.2 typedef重定义
typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int、char等)和自定义的数据类型(struct等)。
在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。
1. 给已知数据类型long起个新名字,叫byte_4,定义方式如下:
typedef long byte_4;
2. 结构体重定义
结构体重定义有下面两种方式:
struct tagMyStruct
{
int iNum;
long lLength;
};
typedef struct tagMyStruct MyStruct;
这里MyStruct实际上相当于struct tagMyStruct,可以使用MyStruct varName来定义变量。
typedef struct{
char plat_no[3+1];
char plat_name[60+1];
} T_PLAT_PARA;
T_PLAT_PARA s_para ;
可以用T_PLAT_PARA s_para来定义变量s_para。
1.3 inline关键字
1. inline关键字说明
inline为把函数替换为函数展开语句,展开在汇编阶段开始。函数的展开是由编译器决定的,这一点对程序员而言是透明的。只有代码很短的情况下,函数才会被展开,递归调用函数不会被展开。如果过度地使用inline关键字,编译器将不会展开函数,以防止代码体积的恶性膨胀。
2. inline关键字举例
假设有如下内联函数:
inline int add(int a, int b)
{
return a+b ;
}
int c ;
当调用c=add(1,9)时函数语句展开变为c=1+9。
1.4 条件编译
1. 条件编译的三种方式
预处理程序提供了条件编译的功能。可以按不同条件去编译不同的程序部分,因而产生不同的目标代码文件,这对于程序的移植和调试是很有用的。
条件编译有三种形式,具体说明如下。
(1) 第一种形式
#ifdef 标识符
程序段1
#else
程序段2
#endif
它的功能是,如果标识符已被#define命令定义过则对程序段1进行编译;否则对程序段2进行编译。如果没有程序段2(它为空),格式中的#else可以没有,即可以写为:
#ifdef 标识符
程序段
#endif
用“#define 标识符”来表明对标识符进行了定义,如“#define XXX”表明对标识符XXX进行了定义。
(2) 第二种形式
#ifndef 标识符
程序段1
#else
程序段2
#endif
与第一种形式的区别是将“ifdef”改为“ifndef”。它的功能是,如果标识符未被#define命令定义过则对程序段1进行编译,否则对程序段2进行编译。这与第一种形式的功能正相反。
如果防止某标识符被重复定义,实现方法如下:
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif
(3) 第三种形式
#if 常量表达式
程序段1
#else
程序段2
#endif
它的功能是,如常量表达式的值为真(非0),则对程序段1进行编译,否则对程序段2进行编译。
2. #if与if的区别
#if 是在编译时起作用,进行条件编译;而if是在程序运行时起作用,进行条件判断。
3. #if可经常使用的场合
我在项目中,经常使用#if 0注释掉程序中一段语句,用#if 1打开程序中一段语句,这比/* */注释更清晰更好使用,因为/* */不能嵌套。使用方法如下:
#if 0
程序段
#endif
1.5 头文件的使用
1. 防止头文件重复包含
假设头文件名为somefile.h,可在头文件名前后加__,并将头文件名大写,然后按如下方式定义防止头文件重复包含问题。
#ifndef __SOMEFILE_H__
#define __SOMEFILE_H__
... ... // 声明、定义语句
#endif
2. 头文件的使用建议及说明
系统头文件用<>符号进行包含,自定义头文件用" "符号进行包含。
建议将全局函数和类型定义集中于一个头文件中,方便程序引用;将常用的系统头文件集中在一个自定义的头文件中,方便程序引用。
没有正确包含库函数头文件有时编译不通过;有时编译通过,但会产生警告信息;有时编译通过连警告信息都不产生;没有正确包含头文件时少量时候代码执行时会产生莫名其妙的错误。
摘录自《深入浅出Linux工具与编程》