《C原语言深度剖析》——关于define

原文地址:https://zouchanglin.github.io/2018/05/03/2018050301/

define 综述

1、函数式宏定义的参数没有类型,预处理器只负责做形式上的替换,而不做参数类型检查,所以危险性高;但因为省去了函数的调用,返回,释放,所以效率比自定义函数高;
2、调用真正函数的代码和调用函数式宏定义的代码编译生成的指令不同。
如果MAX是个普通函数,那么它的函数体return a > b ? a : b; 要编译生成指令,代码中出现的每次调用也要编译生成传参指令和call指令。而如果MAX是个函数式宏定义,这个宏定义本身倒不必编译生成指令,但是代码中出现的每次调用编译生成的指令都相当于一个函数体,而不是简单的几条传参指令和call指令。所以,使用函数式宏定义编译生成的目标文件会比较大。
3、在执行复杂功能时,如递归,函数式宏定义往往会导致较低的代码执行效率。
尽管函数式宏定义和普通函数相比有很多缺点,但只要小心使用还是会显著提高代码的执行效率,毕竟省去了分配和释放栈帧、传参、传返回值等一系列工作,因此那些简短并且被频繁调用的函数经常用函数式宏定义来代替实现。

define定义符号

定义比较长的关键字

#define reg register

用更加形象的的符号替代另一种实现

#define do_forever for( ; ; )

在写switch语句的时候自动把break加上

#define CASE break;case

打印日志

#define DEBUG_PRINT >printf(“file:%s\tline:%d\tdate:%s\ttime:%s\n”,FILE,_LINE__,DATE,TIME)

使用宏时候的提示:所有对于数值表达式求值得宏定义都应该用这种方式加上括号,避免在使用宏的时候由于参数中的操作符之间不可预料的相互作用。

define 替换

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

  • 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
  • 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
  • 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

注意:

  • 宏参数和#define 定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归。
  • 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

“#”与”##”

使用”#”把宏参数变成对应的字符串

1
2
3
4
5
6
7
8
9
#define PRINT(FORMAT, VALUE)\
	printf("the value of "#VALUE" is "FORMAT"\n", VALUE)

int main(void){
	//这里只有当字符串作为宏参数的时候才可以把字符串放在字符串中
	PRINT("%d", 5 + 10);
	system("pause");
	return 0;
}

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

它允许宏定义从分离的文本片段创建标识符。
注意:这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。
接下来说说宏与函数
宏通常被应用于执行简单的运算。比如在两个数中找出较大的一个:

1
#define GET_MAX(a,b) a>b?a:b

用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。
更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用于来比较的类型。宏是类型无关的

和函数相比宏的缺点

  • 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
  • 宏是没法调试的。
  • 宏由于类型无关,也就不够严谨。
  • 宏可能会带来运算符优先级的问题,导致程容易出现错

宏的其他注意事项

宏可以传类型,但是函数不行

1
2
3
4
5
6
7
8
9
10
11
12
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>

#define MALLOC(num, type)\
	(type *)malloc(num * sizeof(type))
int main(void){

	int* p = MALLOC(3, int);
	system("pause");
	return 0;
}

带副作用的宏参数

1
2
3
4
5
6
7
#define MAX(a,b) ((a)>(b)?(a):(b))
int main(int argc,char** argv){
      int a = 5;
      int b = 8;
      int z = MAX(a++,b++);
      printf("a=%d b=%d z=%d\n",a,b,z);
}

上例就说明了带副作用的宏参数的危害

有时不把宏写成全部大写,是为了让别人把它当做函数理解

函数和宏的对比

属性 #defien宏 函数
代码长度 每次使用时,宏代码都被插入到程序中,除了非常短的宏之外,程序的长度将大幅度增长 函数代码只会出现在同一个地方,每次使用这个函数时都会调用这段代码
执行速度 更快 存在函数弹栈、压栈的开销
操作符优先级 宏参数的求值实在所有周围表达式的上下文环境里,除非它们加上括号,否侧邻近操作符的优先级可能会产生不可预料的后果 函数参数只在函数调用时求值一次,它的结果值将传递给函数,表达式的求值很容易预测
参数求值 参数每次用于宏定义时, 它们都将重新求值,具有副作用的参数可能会产生不可预料的结果 参数在函数调用前只求值一次,在函数中多次使用参数并不会带至多种求值过程,参数的副作用并不会造成任何的问题
参数类型 宏与类型无关,只要对参数的操作是合法的,它可以适用于任何参数类型 函数参数与类型有关,如果参数的类型不同,就需要使用不同的函数,即使它们的逻辑任务是相同的
调试 宏在预处理阶段就已经替换,所以无法执行调试 函数是可以调试的
其他 宏不具备函数的性质,不能递归 函数可以递归

-------------本文结束感谢您的阅读-------------

猜你喜欢

转载自blog.csdn.net/m0_38032942/article/details/81109327
今日推荐