编译+链接+预处理详解

目录

一、程序的翻译环境和执行环境

​编辑二、预定义符号

三、#define 定义宏 

四、#define 替换规则

注意:

替换步骤:

五、#和##

六、宏和函数的对比 

七、#undef

八、头文件被包含的方式

(一)头文件被包含的方式一共分为两种:

(二)如何避免重复嵌套文件包含


一、程序的翻译环境和执行环境

二、预定义符号

__FILE__      //进行编译的源文件

__LINE__     //文件当前的行号

__DATE__    //文件被编译的日期

__TIME__    //文件被编译的时间

__STDC__    //如果编译器遵循ANSI C,其值为1,否则未定义

#include<stdio.h>
int main()
{
	printf("%s \n", __FILE__);
	printf("%d \n", __LINE__);
	printf("%s \n", __DATE__);
	printf("%s \n", __TIME__);
	

	return 0;
}

三、#define 定义宏 

#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义 宏(define macro)。

例如:

#define SQUARE( x )  ((x) *(x))

定义一个SQUARE宏定义计算一个数的平方 

注意:1、参数列表的左括号必须与name紧邻。

           2、在使用宏定义的时候不要吝啬使用括号,以确保表达式的计算顺序不被改变,(因为传                     的参数可能是是一个表达式)

比如在下面的一个代码:

int a = 5;
printf("%d\n" ,SQUARE( a + 1) );

加上括号 ,参数替换后:((5+1)*(5+1))=36;

未加括号 ,参数替换后:5+1*5+1=11

通过这个实例看的出来,为了确保宏定义能够按照程序员的意愿进行计算,我们一定要多加括号!(重要)

四、#define 替换规则

注意:

1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。

2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

替换步骤:

1、在调用宏的时候,先对参数进行检查,如果有包含任何其他#define定义的符号,如果有,进行替换

2、替换后的参数插入到程序原本的位置

3、最后再对结果文件进行扫描,看看是否包含任何其他#define定义的符号,如果是,重复上面处理

五、#和##

#:将宏定义的参数变成对应的字符串

例如:

int i = 10;
//定义宏定义
#define PRINT(FORMAT, VALUE)\
 printf("the value of " #VALUE "is "FORMAT "\n", VALUE);
...
//使用宏定义
PRINT("%d", i+3);/

运行结果: 

 the value of i+3 is 13

##:将##两边的符号合成一个符号

例如:

#define ADD_TO_SUM(num, value) \
 sum##num += value;
...
ADD_TO_SUM(5, 10);//作用是:给sum5增加10.

这里的 sum##num += value   变成了sum5 += 10

六、宏和函数的对比 

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

对于上面简单的功能,宏是比函数更加高效

宏的优点:1、由于用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的                          时间更多。 所以宏比函数在程序的规模和速度方面更胜一筹。

                  2、函数的参数必须声明为特定的类型。 所以函数只能在类型合适的表达式上使用。反                          之这个宏怎可以适用于整形、长整型、浮点型等可以 用于>来比较的类型。 宏是类                          型无关的。

宏的缺点:1、宏替换后将宏定义代码直接插入到程序中,除非宏比较短,不然的话会大大增加程                          序的长度

                   2、宏是无法调试的

                   3、因为宏是类型无关的,所以不够严谨

                   4、宏可能会带来运算符优先级的问题,容易导致出错,所以我们在使用宏定义的时                            候,多加括号

七、#undef

这条指令用于移除一个宏定义。

例如:

#define NUM 10
#undef NUM
int main()
{
	int a = NUM;

	return 0;
}

定义了一个宏定义,又将宏定义移除,宏定义在代码中就不可以使用了 

八、头文件被包含的方式

(一)头文件被包含的方式一共分为两种:

1、本地文件包含——#include“xxx"

查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标 准位置查找头文件。 如果找不到就提示编译错误。

2、库文件包含——#include<xxx>

查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。

当然,库文件包含也可以用“ ”来包含,但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。

(二)如何避免重复嵌套文件包含

一共有两种办法:

1、条件编译:

每个文件开头这样写:

#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif   //__TEST_H__

这段代码的意思就是,如果第一次引用头文件,就会定义一个__TEST_H__的宏,如果下次还引用这个文件,#ifndef __TEST_H 就会判断已经__TEST_H__这个宏已经被定义,程序就不会再进行了,这样就做到了避免文件多次包含

 1、在开头写下这段代码:

#pragma once

就可以避免头文件的重复引入。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿拉保

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值