程序的 *** 作(预处理)第二部分

程序的 *** 作(预处理)第二部分,第1张

四、预定义符号介绍

__FILE__       // 进行编译的源文件 __LINE__     // 文件当前的行号 __DATE__     // 文件被编译的日期 __TIME__     // 文件被编译的时间 __STDC__     // 如果编译器遵循 ANSI C ,其值为 1 ,否则未定义

上面这些代码都是C语言内置的代码。

下面重点介绍#define和#include这两个预处理指令

 #define

1、#define定义标识符

2、#define定义宏

1、#define可用于定义某些标识符,可以在一定程度上增加代码的可读性。

举个例子:

#define MAX 100

这样所有需要100的地方都可以用MAX来替代。

#define定义的标识符甚至可以是一段代码,这是C允许的。

注意:不要在#define语句后随意添加分号,否则会出现错误,如:

#define M 100;
int main()
{
    int a = M;
    return 0;
}

 这时编译器会认为分号也是标识符的一部分,替换后的a就变成了 100;;从而产生了错误,所以不要在#define语句后面随便加上分号!

2、#define定义宏

#define定义宏的具体方式是:

#define name( parament - list ) stuff, 其中的name是宏名称, parament - list 是一个由逗号隔开的符号表,stuff是语句项。

注意:符号表外面的括号必须紧贴name,否则会被解析为stuff的一部分。

例: #define Add(a,b) a+b

#define定义的宏有一个非常易错的地方(反正我是错了好几次),就是宏定义的参数是直接替换上去的,有时甚至没考虑运算符之间的运算顺序。

比如:

#define SQUARE(X) X*X
int main()
{
    int a = 5;
    int b = SQUARE(a+1);
    printf("%d",b);//打印的结果是什么?
    return 0;
}

或许你会说代码的结果是36,那恭喜你,答错了~ 打印的结果是11,为什么呢?

因为#define定义宏是将参数直接替换,并不是将参数中表达式的结果运算完再代入,这个代码中替换完b = a+1*1+a 所以应该是5+1*1+5 = 11,并不是36。

如果我们想要刚开始我们猜测的结果,就应该在宏定义内的X两边加上括号——

#define SQUARE(X) (X)*(X)
int main()
{
    int a = 5;
    int b = SQUARE(a+1);
    printf("%d",b);
    return 0;
}

但是这样还是有问题,比如将b后面的语句修改为

#define PLUS(X) (X)+(X)
int main()
{
    int a = 5;
    int b = 10*PLUS(a);
    printf("%d",b);
    return 0;
}

好吧,这个代码的结果也并不是我们所预期的100,而是55,这是因为宏替换完的结果是10*5+5=55,如果我们需要得到100,应做如下的修改:

#define PLUS(X)  ((X)+(X) )//多加上一对括号
int main()
{
    int a = 5;
    int b = 10*PLUS(a);
    printf("%d",b);
    return 0;
}

讲了这么多,总结一句话就是写宏定义语句时候一定不要吝啬自己的括号,否则就会出现不在预期内的结果。

#define的替换规则

#define 替换规则 在程序中扩展 #define 定义符号和宏时,需要涉及几个步骤。

1. 在调用宏时,首先对参数进行检查,看看是否包含任何由 #define 定义的符号。

如果是,它们首先被替换。

2. 替换文本随后被插入到程序中原来文本的位置。

对于宏,参数名被他们的值所替换。

3. 最后,再次对结果文件进行扫描,看看它是否包含任何由 #define 定义的符号。

如果是,就重复上 述处理过程。

注意: 1. 宏参数和 #define 定义中可以出现其他 #define 定义的符号。

但是对于宏,不能出现递归。

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

 字符串常量中如果出现了#define定义的标识符,将不会被替换,因为字符串常量不可被修改。

 #和##的作用

 当字符串作为宏的参数时候,可以被直接替换进字符串内部,如

#define PRINT(FORMAT, VALUE)\
 printf("the value is "FORMAT"\n", VALUE);
int main()
{
	PRINT("%d", 10);
	return 0;
}

PRINT的作用相当于 printf("the value is %d\n",10)。

#的作用能将任意参数转化为字符串放入字符串内,比如:

#define PRINT(FORMAT,VALUE)\
	printf("the value of " #VALUE " is "FORMAT "\n", VALUE);
int main()
{
	int i = 0;
	PRINT("%d",i + 3);
	return 0;
}

打印结果:

 如果没有#,打印的结果将会是:

字符串中的VALUE无法随着传入参数的变化而变化,一直都会是VALUE。

 

##的作用是将两侧的分离字符和为一个字符

比如:
 

#define TEST(p,value)
    sum##value = p; 
int main()
{
    int sum5 = 0;
    TEST(10,5);
    printf("%d",sum5);
    return 0;
}

 

显然value是5的时候sum##value就变成了sum5,它在宏内被赋值为10,所以代码的结果是10。

宏显然是和函数有点相似的,那宏与函数各自的优缺点是什么呢?

对于一些比较小型的代码,我们常用宏来解决,比如:求两个数的较大值,求和,求商...

不用函数的原因是,函数的调用往往需要在栈上开辟空间,函数开辟栈帧的创建和销毁,往往需要占用空间和时间,不如宏来的效率高。

第二个原因是函数的参数是指定类型的,比如是double类型,那就只能求double类型数的较大值。

而宏是不受参数限制的,也就是说宏是参数类型无关的。

 

当然宏也是有一定缺点的:

1、宏没有参数检查,所以不够严谨。

2、每次使用时,宏代码都会被插入到程序中。

除了非常 小的宏之外,程序的长度会大幅度增长

3、宏没有办法调试,因为调试的时候已经生成了可执行文件,宏在预处理阶段已经被替换掉了。

4、由于宏的直接替换,会带来一些运算优先级的不同,导致一些错误。

5、宏不可以递归

 宏还能做到一些函数做不到的事情——宏的参数可以是类型,而函数不能。

这次的分享就到这,下次给大家分享条件编译的内容!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/674865.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-19
下一篇 2022-04-19

发表评论

登录后才能评论

评论列表(0条)

保存