c语言的预处理/条件编译

c语言的预处理/条件编译

游戏|数码彩彩2024-02-25 7:36:13452A+A-
c语言的预处理/条件编译,最全整理看这里

 

语言的编译过程中第一步进行的就是预编译了,预编译中就是执行#开头的语句,这些处理就是本篇总结的知识点,预处理和条件编译。

通俗的讲预处理就是编译器自动的帮我们上去整理一遍代码,它依据的规则(我们交代给他的嘱托)就是预处理指令和条件编译的指令。

编译器根据指令,将需要包含的代码整体复制放到要包含的文件中,检查条件编译的条件是否成立,删除用不到的代码,留下有用的代码,预处理的过程仅此而已。

要让编译器准确的完成我们交给他的任务,我们就必须准确的给它下达正确的命令,这些命令就是下面总结的内容了。只是常用到的知识点的总结,水平有限有错误在所难免欢迎指正。

1.预处理是c文件在编译前的处理过程。

这个过程包括如下几个内容:

  1. 删除注释
  2. 插入被#include包含的文件的内容
  3. 替换有#define定义的符号
  4. 确认条件编译具体该进行编译的部分

2.预定义器定义的符号(现成的)

这些符号是编译器自带的,不用我们自己定义,直接使用就可以。

一般在我们调试程序时,打印这些信息时使用它们。

  • __FILE__ :要进行编译的文件的文件名
  • __LINE__:文件该符号所在行得行号
  • __DATE__:文件被编译的日期
  • __TIME__:文件被编译的时间

3.#define 定义符号名的用法

  1. 为数值命名一个符号。

Eg: #define name stuff (很容易跟typedef搞混前后顺序)

用法:

  • 一般在程序的头部定义该语句,之后就可以用name去代替stuff写代码了。编译时语句中如果出现name,就会被预编译器替换成stuff。

b. 文本替换

Eg1:

#define Ret returnval 为简化名字的书写和阅读方便

Eg2:

#define Loop while(1); 可以替换一个语句,替换语句时注意“;”的问题

4.宏的使用

所谓的宏,其实是#define的文本替换的活用。

本质是是在文本替换的基础上,再追加一步,参数的替换。

Eg 1:

#define MAX(a,b) ( (a)>(b)?(a):(b))

在代码中的使用过程是这样的:

第一步:文本替换

maxnum=MAX(2,4); —> ( (a)>(b)?(a):(b));

第二步:参数代入

maxnum=( (2)>(4)?(2):(4));

到此预处理器的宏处理阶段完成。

5.宏的参数代入过程看起来很像是与函数的参数传递过程,有什么不一样呢?

  • 1.宏的参数不限制类型使用参数时也不需要声明类型,但是函数的参数必须声明(规定)参数类型。

宏的这个特点有时可以带入函数无法带入的参数类型,进行运算。

Eg :

#define MALLOC(n,type) ( (type)*) malloc( (n) * sizeof(type) ) )

带个数的结果:MALLOC(5,int ) —>(int*)malloc( (5)*sizeof(int) )

这里的参数type,函数是无法用参数进行传递的。

  • 2.实现机制不同,宏是通过预处理时文本替换实现,函数是通过运行时临时调用和返回,这就带来他们的优缺点:
  • 宏:不需要调用开销,执行频繁的简单计算,缺点会增加代码体积。
  • 函数:比较耗费系统资源,但不增加代码体积。
  •  

6.#运算符和##运算符

#:将将宏参数插入字符串中。

Eg :

#define PRINT(a) pirntf("the value of a is %d.n",(a) )

int b=2;

PRINT(b); => the value of a is 2.

#define PRINT(a) pronrf("the calue of "#a"is %d.n",(a) )

int b=2;

PRINT(b); => the value of b is 2.

  • ##:粘合剂 字符合并

Eg :

#define RENAME(A) NewName ## A

RENAME(3); => NewName3

7.宏的注意点

  • 宏名全大写字母表示
  • 每个参数必须加()
  • 宏的整体外面加一层括号
  • 不要在宏的末尾加分号
  • 替换文本(stuff)的内容太多可以用“”连接换行
  • 宏定义时:宏的参数列表(括号的左边)与宏名(name)之间不能有空格。在代码中使用无此规定
  • 宏的参数不能用 自增、自减等运算符,会有副作用

8.条件编译

  • 用法1:

#if 常量表达式

statements

#endif

Eg :常用的调试语句

#define DEBUG 1 //1:打开 ,0:屏蔽

#if DEBUG

printf("File is %s,Line is %d",__FILE__,__LINE__);

#endif

这样debug语句不会 影响程序本身运行。

  • 用法2:选择编译

#if 常量表达式

Statements;

#elif 常量表达式

Statements;

#elif 常量表达式

Statements;

#else

Statements;

#endif

预处理器从第一条依次判断常量表达式的真假(0为假),

  • 从前往后当一个为真时,就编译该分支的语句,忽略之后的所有 分支的语句,当所有的常量表达式均为假,才编译“#else”分支的语句。
  • 注意:这里的常量表达式,一般用是一个用#define 声明的常量,不要错误使用全局变量,或是const 修饰的只读变量。

9.文件包含

  • a.你编写被包含文件(.h)时,在文件头部加上:

#ifndef _FILENAME_H

#define _FILENAME_H

XXXX(其他语句)

#endif

来防止重复包含

  • b.包含文件时的写法分两种:
  • #incldue <FileName.h>
  • 使用尖括号的,这是指包含的文件时自带的库函数头文件
  • #include“FileName.h”
  • 使用双引号的,这是包含用户自己编写的头文件
  • 这两种的区别就是编译器搜索文件的方式不同。

在结束的时候,还是再说一下学习过程中的感想,预编译这块很容易被我们忽视,初学起来感觉难度并没有很大,但是这里可以深究的地方还是很多的,多看一看这方面的例子,或者稍复杂些的宏定义,就会有很容易掉坑里的感觉,而且这里出现的Bug是很不容易被发现的。在程序编写时,恰当的使用宏和条件编译会让程序的结构得到优化,在uboot中大规模的使用宏定义条件编译相关的语句来实现配置选择和功能的选择,所以说这部分的知识很重要,需要取细细研究。争取做到准确,熟练,快速的看懂,看的对宏的意图!

点击这里复制本文地址 版权声明:本文内容由网友提供,该文观点仅代表作者本人。本站(https://www.angyang.net.cn)仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件举报,一经查实,本站将立刻删除。

昂扬百科 © All Rights Reserved.  渝ICP备2023000803号-3网赚杂谈