程序的预处理详解

目录

1.编译环境和运行环境

2.预处理

#define

#和##

带副作用的宏参数

#undef

条件编译

文件包含

总结


1.编译环境和运行环境

翻译环境:在这种环境中,源代码被替换成可执行程序;

运行环境:在这种环境中,可执行程序被执行,最终产生运行结果。

如下图:

  •  编译环境:

预编译过程:实现头文件的包含、注释的删除、define 符号的替换;

编译:语法分析、词法分析、语义分析、符号汇总、将C语言的代码转换成汇编代码;

汇编:将汇编指令转换为二进制指令、形成符号表;

链接:合并段表、符号表的合并和重定位。

  • 运行环境:

将程序导入内存,程序便开始执行,接着便调用main函数,执行代码,终止程序。

2.预处理

#define

  • #define定义标识符

直接替换

#define MAX 9197
#define STR "bjyx"
int main() 
{
	printf("%d\n", MAX);//9197
	printf("%s\n", STR);//bjyx
	return 0;
}

注意:define定义标识符的时候,最后不要加上;

  • #define定义宏

#define MAX(x,y) x>y?x:y
int main() 
{ 
	printf("%d\n", MAX(2,3));
	return 0;
}

注意:定义的宏name必须与左边列表的括号紧密相连

下面我们来看一段代码:

#define SQUARE(x)  x*x
int main() 
{ 
	printf("%d\n", SQUARE(3 + 1));
	return 0;
}
//那么此时的结果还是16吗?

答案是否定的,经过编译我们发现答案是7,原因:define定义宏也是进行直接替换的 ,3+1*3+1=7。

经过修改:

#define SQUARE(x)  (x)*(x)
int main() 
{ 
	printf("%d\n", SQUARE(3 + 1));
	return 0;
}

我们再来看一组代码:

#define DOUBLE(x)  (x)+(x)
int main() 
{ 
	printf("%d\n", 10*DOUBLE(3));//60?
	return 0;
}

经过编译,发现答案是33,这是因为乘法的优先级先于宏定义的加法。10*(3)+(3)=33。

修改:

#define DOUBLE(x)  ((x)+(x))
int main() 
{ 
	printf("%d\n", 10*DOUBLE(3));//33
	return 0;
}

 所以,在宏定义数值运算的时候,最好都应该用上述方式加上括号。

  • #define替换规则

直接替换!

1、在调用宏时,首先检查参数,如果参数包含#define定义的符号,首先被替换;

2、替换文本,被插入程序中文本原来的位置,对于宏,参数名被他们原来的值所替换;

3、对结果文件进行扫描,如果包含#define定义的符号,则进行上述步骤。

扩展:

1、宏参数和#define定义中可以出现其他#define定义的符号,但是不能进行递归;

2、预处理检查#define定义的符号时,字符串不会被搜索

#define MAX 10;
int main(){
    char str[]="hahaMAX";//不会被替换
    return 0;
}

#和##

#可以把一个宏参数变成对应的字符串

首先,我们看一下下述代码:

printf("bjyx""happy""\n");//biyxhappy
//字符串具有自动连接的功能
int main() 
{   
	int a = 1;
	int b = 2;
	double c = 3.0;
	printf("the value of a is %d\n", a);
	printf("the value of b is %d\n", b);
	printf("the value of c is %d\n", c);
//上述代码可以通过函数来实现吗?---显而易见,不可以!
	return 0;
}

我们能否用宏来实现呢?此时,我们就需要运用#了。

#define PRINT(number,format)  printf("the value of "#number" is " format "\n",number)
// #number相当于“a”/"b"/"c"...
int main() 
{   
	int a = 1;
	int b = 2;
	double c = 3.0;
	PRINT(a, "%d");
	PRINT(b, "%d");
	PRINT(c, "%lf");
    return 0;
}

 ##可以把位于它两边的符号合并成一个符号

#define COMBINE(a,b)   a##b
int main() 
{   
	int love = 1823;
	printf("%d\n", COMBINE(lo, ve));//1823
	return 0;
}

注意:它的连接必须产生一个合法的标识符,否则结果是未定义的。

带副作用的宏参数

如果宏参数带有副作用,就会给程序带来不可预测的危险。

例如:

#define MAX(x,y) ((x)>(y)?(x):(y))
int main(){
    int a=1;
    int b=2;
    printf("%d\n", MAX(a++, b++));//3
	printf("%d\n", a);//2
	printf("%d\n", b);//4
    return 0;
}

 不仅返回值改变,a,b的值都改变了。

#undef

用语移除一个宏指令。

条件编译

由于有条件编译,是的我们在编译一个程序是将一组(条)语句编译或者放弃是很方便的。

下面,我们来简单介绍一下条件编译:

常见的条件编译指令:

#if  常量表达式(由预处理求值)

#endif

例如:#define MAX 1

          #if   MAX

          #endif

多个分支的条件编译

#if   常量表达式

#elif  常量表达式

#else  常量表达式

#endif

判断是否被定义

#if  defined(symbol)

#ifdef   symbol

(如果被定义了怎么怎么样)

#if !defined(symbol)

#ifndef  symbol

(如果没有被定义)

嵌套指令

#if defined()

    #ifdef()

    #endif

#elif defined()

    #ifdef()

    #endif()

#endif

总的来说,类似于我们之前学的if ...else语句。

文件包含

#include<stdio.h>
#include"game.h"

上述都属于文件包含,那为什么有的使用<>,而有的是用“”呢?

<>从库文件中寻找(从编译选项指定的库目录中寻找)

“”从当前编译的源文件所在目录中去寻找,找不到就从库文件中寻找。

嵌套文件包含:

嵌套文件包含是指一个文件包含另外一个文件,同时被包含的文件又包含了另外一个文件。

如果头文件嵌套的话,会造成包含很多不必要的头文件。

1、使用条件编译:

ifndef/defined /endif

2、另外,还可以使用:

#pragma once

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


总结

祝大家顺颂时宜,百事从欢。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值