C语言的预编译

Technorati 标签: C, 预编译, , 文件替换, 条件编译

         由“源代码”到“可执行文件”的过程包括四个步骤:预编译编译汇编链接。所以,首先就应该清楚的首要问题就是:预编译只是对程序的文本起作用,换句话说就是,预编译阶段仅仅对源代码的单词进行变换,而不是对程序中的变量、函数等。

        预编译指令的基本知识不作详细介绍,只稍作汇总,重点是后面的我能想到的 使用时的注意事项。

1. 基本内容

预编译指令基本分类如下

类别

指令

预定义符号__FILE__、__LINE__、__DATE__、__TIME__、__STDC__
#define
文件包含#include
条件编译#if、#elif、#else、#ifdef、#ifndef、#endif

还有一些指令,名称和功能如下表:

指令功能
#空指令
#undef移除一个空定义
#error停止编译,并生成错误信息
#line修改__LINE__和__FILE__的值
#progma允许编译器提供额外功能

在定义宏的时候,有两个运算符

运算符功能
#将宏参数转换为字符串
##将多个符号连接成一个标识符

2. 宏定义

1. 一般在宏定义的结尾不加分号

        我们在使用的时候,要加上分号,像我们平时写语句一样。

2. 注意加括号

        在有参数的空定义中,如果含有数值运算,那么就要在“宏整体”和“宏参数”两端都要加上括号。
        如:#define max(a, b) ((a)+(b)); 

3. 注意空格

        在有参数的宏定义中,注意“宏名称”和“参数列表”之间不能有空格
        如:#define max (a, b) ((a)+(b));  在"max”和”(a, b)”之间不能有空格。

4. 不要使用有副作用的参数区调用宏。

        常见的有副作用的参数有:a++,getchar()等。
        如:宏定义为#define max (a, b) ((a)+(b));  那么使用max(i++, j++)调用该宏,会造成 i 或 j 中的一个值增加2,而不是我们期望的 1。

5. 可以使用编译器选项 添加宏 和 移除宏。

        我使用的是gcc,添加宏的指令是”-D”,移除宏的指令是”-U”。

6. 宏参数替换的时候,不会替换字符串中的字符

        即不会替换双引号之间的字符,其他的都会被替换,包括单引号之间的。

7. 可以使用#将 宏参数的值 转化为字符串

        直接使用#,是将宏参数的名称转化为字符串。利用下面的技巧(增加一个过渡宏),可以将“宏参数的值”转化为字符串(当宏参数有值时,这时的宏参数常常也是一个宏)。

#include <stdio.h> 
#include <stdlib.h> 

#define NUMBER ten   /* 宏名称为NUMBER,宏的值为ten */

#define Str(x) #x  
#define XStr(x) Str(x)  /* 增加的一个 过渡宏 */

int main(){
    
    printf("Str(NUMBER) == %s  /n", Str(NUMBER)); 
    printf("XStr(NUMBER) == %s  /n", XStr(NUMBER));
       
    system("pause");
    return EXIT_SUCCESS;
}

输出结果为:

Str(NUMBER) == NUMBER
XStr(NUMBER) == ten

8. 使用##运算符来实现标识符连接

        不过,不建议使用操作符##来连接标识符,因为这个容易是程序可读性大大降低。

3. 文件包含

1. 要将头文件的定义在保护条件中。

        目的是为了防止重复包含头文件。如果你查看过gcc或者其他编译器的源代码,你一定对这个非常熟悉。

        例如,你要编写一个头文件,myheader.h,那么你的头文件的内容形式应该为:(定义一个_MYHEADER宏)

#ifndef _MYHEADER
#define _MYHEADER 1

/* 中间是你的头文件内容   */

#endif  /* _MYHEADER */ 

2. 注意windows系统和Unix系统的路径符号不同

        可以再#include中指定路径来包含文件,例如 #include “../head.h”。但是注意,windows中使用反斜线”/”作为路径分隔符,而Unix系统使用的是斜线”/”

3. 可以使用 编译器选项 来设置搜索路径。

        我使用的gcc,使用的-Idir选项,例如: -I"D:/Dev-Cpp/include"。

4. 条件编译

1. #ifdef等价于#if defined(),#ifndef等价于#if !defined()。

2. 在#if中可以使用逻辑操作符(&&、||、!)。在#ifdef 中是不可以使用的,这也是#if的优越点。

#include <stdio.h> 
#include <stdlib.h> 

#define A 1
#define B 0

int main(){
    
    #if defined( A ) && defined( B )
        printf("test logic operation in #if  /n");  /* 如果上面的逻辑判断成立,那么将打印出一句话;如果不成立,那么就不会打印这句话 */
    #endif
    
    system("pause");
    return EXIT_SUCCESS;
}

运行结果:

test logic operation in #if

3. sizeof(int)在预编译阶段是不会被求值的。

        只要知道“预编译阶段”在真正的“编译阶段”之前,就很容易理解了。预编译阶段只是对组成源代码中的字符进行作用,从某种意义上来说,它有时甚至不知道它的操作对象是什么,它只是按照既定的规则执行替换。

        sizeof(int),无论是sizeof的解析,还是类型的解析,都是在“编译阶段”才开始的,编译阶段知道它的操作对象是什么。

下面的代码是错误

#if sizeof(int) == 2
    printf("precompile sizeof(int)"); 
#endif

5. 额外注意

        把一个预处理指令成多行的形式,要使用符号”/”,并且在该符号后面应紧跟换行符。而非预处理指令代码行不需要使用该符号,直接换行即可。 原因:编译阶段会自动忽略空白符,而预编译阶段不会。

 

本文链接:http://blog.csdn.net/daheiantian/archive/2011/02/09/6243993.aspx

 

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值