#define#include 定义 预处理入门详解(C语言)

本篇博客会解答如下问题:


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

当定义过一次后,不满足条件,就不会再次引入了。


本篇关于 预处理 到此就结束了。

如果你觉得本文还不错,请你给我一个赞吧!感谢~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值