本篇博客会解答如下问题:
1.#include <>与""的区别
2.头文件中ifdef/ ifndef/define/endif分别的作用是什么
3.defined 定义需要加 ';' 吗
4.#pragme once是干什么用的
5.define定义常量
6.define定义宏
本篇博客共为 2800 余字,问题都在博客当中做得回答,目录有部分问题快捷键。
前言:根据ANSI C的任何一种实现,为翻译环境+运行环境。
翻译环境由编译和链接两个大类组成,编译可以拆解为预编译、编译、汇编三个过程。
{预编译->编译->汇编}->链接 最后成为可执行程序
本文讲述的是.c文件->.s文件的预处理阶段。预处理阶段主要处理源文件中#开始的预编译命令,如#include/#define。
在此过程中,1.将展开所有define,并展开所有宏定义
2.删除所有注释
3.处理 #include 预编译指令,头文件的内容展开。整个过程是递归进行的,被包含的头文件也可能包含其他文件。
4.添加行号和文件名标识,方便后续编译器生成调试信息
5.处理所有的条件编译指令,如:#ifndef / #endif等,保留#pragma的编译器指令
例: test.c
test.i
我们可以看到处理了#include预编译命令(.i图片上半部分),将define定义的常数全部替换,增加了行号和文件名标识。
基本语法: #define name stuff
重要:define的作用就是纯替换,在进行预编译时,将name->stuff全部替换
问题1:define后面需要加 ';' 吗?
#define MAX 1000;
...
if(条件)
max = MAX;
else
...
建议不要 ';' ,如遇见上述代码,在if没有大括号的前提下,只能执行一条语句,define替换后,为max=1000;;,出现语法错误。
当我们知道了define就是纯替换,我们可以做很多事情
define宏定义
如果我们这样定义:
#define MUL(a) a*a
当我们使用这个定义时:
int c=5;
int b;
b=MUL(c);
上述代码变为:(.i文件)
int c=5;
int b;
b=c*c;
#define机制包括了⼀个规定,允许把参数替换到文本中,这种实现通常称为宏
在我们使用宏中:要及时添加括号,保证替换后进行运算时运算优先级不发生改变。
如:让上述代码变为
b=MUL(c+1); --> 本意想变为(c+1)*(c+1)-->但替换后就变为了 c + 1 * c + 1
在定义时加上括号:#define MUL(a) (a)*(a) 就不会出现上述问题了。
那么 #define ADD(x) (x)+(x) 会有问题么?
在我们要使用这样的代码就会出现问题:
a=10;
printf("%d",5*ADD(a)); 如果本意是(10)+(10)后再×5,那么就出现错误了
替换后就为5*(10)+(10)=50+10=60;
当我们将#define ADD(x) ((x)+(x)); 就可以避免这个错误了。
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
MAX(x++, y++)
因为是替换,所以不是像函数一样传参,在进行比较时会产生多余的++赋值,再次改变了数值本身的大小( (x++) > (y++) ? (x++) : (y++) )。这样叫带有副作⽤的宏参数。
上面这个示例MAX(x++,y++)如果是函数也会改变数值,不过是define定义的在( (x++) > (y++) ? (x++) : (y++) )时会给其中一个变量多加一次。
所以,在我们定义时,一定要清楚替换后的运算是如何进行的。
在宏替换时,1.参数有define定义的符号优先替换
2.替换文本插入到程序原来位置。对于宏,参数名被他们的值所替换
3.最后在查看是否包含任何由#define定义的符号,如果有重复上述过程
至此,我们可以有很多种写代码的方式。如:
当要比较大小时,可以直接用宏定义
#define MAX(a,b) ((a)>(b)?(a):(b))
相较于函数,这样就剪掉了调用函数和从函数返回时间,并且不用设置函数返回和比较类型。(宏可以直接比较整形或浮点型等)
#运算符将宏的⼀个参数转换为字符串字面量。它仅允许出现在带参数的宏的替换列表中。#运算符所执行的操作可以理解为”字符串化“。
当我想在printf中当字符串打印,并做为变量时:
#define PT(a) printf("shuchu:" #a "=%d",a);
替换为 printf("shuchu:" "a" "=%d",a);
#a替换为“a” 使其可以以字符串的形式输出
##运算符可以把位于它两边的符号合成⼀个符号,它允许宏定义从分离的文本片段创建标识符。 例如:#define type a 替换时, type-->a type_add 就不能替换为a了,但加上##,type##_add-->a_add
#undef 用于取消定义,在此之后命令不会再替换
#ifdef/#endif 等条件编译
#ifdef MAN
printf("man");
#endif
ifdef条件编译,如果上面进行了 #define MAN 声明,那么执行里面的printf语句。在此条件编译中,只关心有没有进行声明,不关心声明值为多少 #define MAN 0 也是可以的。
#ifndef 如果没有被定义则执行
常见的有:
1.常量表达式由预处理器求值
#if 常量表达式
...
#endif
例:
#define MAN 1
#if MAN
执行...
#endif
2.多个分支的条件编译
#if 常量表达式
...
#elif 常量表达式
...
#else
...
#endif
3.判断是否被定义
#if defined(symbol) 如果定义了,便执行。if defined后定义带括号!!
#ifdef symbol 如果定义了,便执行。 无括号
#if !defined(symbol) 反之
#ifndef symbol
色块相同的两个语句效果相同
4.嵌套指令
#if 常量表达式
#ifdef
...
#endif
#elif 常量表达式
#ifdef
...
#endif
#endif
问题2:#include <>与""的区别,可以直接用""吗?
""先在源文件目录下查找,找不到再在标准位置找。都找不到报错。
<>直接在标准位置找。
可以都用"",不过<>头文件会现在源文件目录下找一遍。
#pragme once是干什么用的
#include引用时,如果写三遍,#include "test.h"。
#include "test.h"
#include "test.h"
#include "test.h"
test.c文件中将test.h包含3次,那么test.h文件的内容将会被拷贝3份在test.c中。
在前面加上#pragme once就可以避免重复引入
同样用条件编译也可以避免,在每个头文件开头写:
#ifdef _TEST_
define _TEST_
头文件内容...
#endif
当定义过一次后,不满足条件,就不会再次引入了。
本篇关于 预处理 到此就结束了。
如果你觉得本文还不错,请你给我一个赞吧!感谢~~
标签:定义,++,C语言,编译,include,替换,define From: https://blog.csdn.net/m0_73640343/article/details/142344704