C语言预处理

来源:互联网 发布:淘宝3d试衣技术 编辑:程序博客网 时间:2024/04/18 17:43
 

所谓预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理是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工具与编程》