【无标题】

一,宏定义用法

①宏常量

  我们最常使用到的#define的用法就是用#define来定义一个符号常量,而要修改时,只需修改#define这条语句就行了,不必每处代码都修改
例:

#include"stdio.h"
#define PI 3.14
#define STR "圆周率约等于"
int main()
{
	printf("%s %f",STR,PI); //预处理时会被替换为 printf("%s %f","圆周率约等于",3.14);
	return 0;
}

②宏语句

  我们还可以用宏定义一条或多条语句
例:

#include"stdio.h"
#define Print printf("hello world!")
int main()
{
	Print;  //预处理时会被替换为 printf("hello world!");
	return 0;
}

③宏函数

  我还可以用宏来定义函数,因为宏定义也可以带参数
例:

#include"stdio.h"
#define Print(str) printf("%s",str)
int main()
{
	Print("这是一个只有一条语句的宏函数!");
    //预处理时会被替换为 printf("%s","这是一个只有一条语句的宏函数!")
	return 0;
}

④其它

1.#undef 是用来撤销宏定义的,用法如下:

#define PI 3.141592654
 
…
 
// code
#undef PI
//下面开始 PI 就失效了

2.使用ifndef防止头文件被重复包含和编译

C语言编译系统包括预处理,编译和链接等部分。

      条件指示符#ifndef检查预编译常量在前面是否已经被宏定义。如果在前面没有被宏定义,则条件指示符的值为真,于是从#ifndef到#endif之间的所有语句都被包含进来进行编译处理。相反,如果#ifndef指示符的值为假,则它与#endif指示符之间的行将被忽略。条件指示符#ifndef 的最主要目的是防止头文件的重复包含和编译。
  千万不要忽略了头件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。

把头文件的内容都放在#ifndef和#endif中不管你的头文件会不会被多个文件引用,你都要加上这个

  #ifndef <标识>

  #define <标识>

  ......

  #endif

<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h


  #ifndef _STDIO_H_

  #define _STDIO_H_

  ......

    #endif

#ifndef xxx //如果没有定义xxx
#define xxx //定义xxx
#endif //结束如果
这个用法主要是在头文件中,主要是为了防止类重复的include,所以在类的头文件之前加上前面两个,用类名替代xxx,在最后加上最后一句

二,宏定义相关作用符

①换行符 "\"

 我们就在每行末尾(除了最后一行)加上"\",代表换行的意思

②字符串化符 "#"

  "#"是“字符串化”的意思,将出现在宏定义中的#是把跟在后面的参数转换成一个字符串
③片段连接符"##"

  “##”是一种分隔连接方式,它的作用是先分隔,然后进行强制连接。在普通的宏定义中,预处理器一般把空格解释成分段标志,对于每一段和前面比较,相同的就被替换。但是这样做的结果是,被替换段之间存在一些空格。如果我们不希望出现这些空格,就可以通过添加一些##来替代空格。
 


三,宏函数的巧用

①类型传递

  我们知道函数虽然可以传递参数,但是却不能把类型作为参数传递,有时我们为了实现函数的复用性,就要使用STL模板,但是我们这个时候还有另外一种选择,就是写成宏函数
例:
一个开辟内存的函数

#define Malloc(type,size) (type*)malloc(sizeof(type)*size)

这个时候,我们只有把类型,容量作为参数传递进行,就可以开辟各种类型的内存了

int *p=Malloc(int,100); //开辟int类型的内存
char *q=Malloc(char,100); //开辟字符类型的内存

②传递数组

  由数组作为函数参数传递时,会失去其数组特性,也就是无法使用sizeof()函数计算出数组的大小,比如我们写一个排序函数,排序时我们不仅需要知道数组的首地址,还需要知道数组的大小,但是仅仅把数组名作为参数传递时,无法得知其数组大小,这时我们的函数就需要传递第二个参数,也就是数组的大小,于是函数就要写成Sort(int *a,int size).但宏函数就可以解决这个问题

 

四,注意事项

① 运算符优先级问题

#define MULTIPLY(x, y) x * y

  这是一个很简单的乘法函数,当计算MULTIPLY(10, 10),结果是100,这个大家都知道,但是当你计算MULTIPLY(5+5, 10)时,你以为结果还是100吗?当然不是,MULTIPLY(5+5, 10)=5+5*10=55,所以结果是55,所以我们写宏函数时要特别注意运算符的优先级,这里稳妥一点的写法应该这样写

#define MULTIPLY(x, y) ((x)*(y))

②宏参数重复调用

#define MAX(a,b) ((a)>(b)?(a):(b))
int a=0;
int b =1;
int c =MAX(++a,++b);

这里很多人都以为是c=MAX(1,2)=2;而实际上上面代码等价于

int c =((++a)>(++b)?(++a):(++b));

可以看到实际上a b都各自加了两次,所以c=1>2?2:3=3,所以结果是3

③分号吞噬问题

#include"stdio.h"
#define FUN(n)\
{\
	while(n>0)\
	{\
		if(n==3)\
			break;\	
	}\	
}
int main()
{
	int num=10;
	if(num>0)
		FUN(num);
	else
		num=-num;
	return 0;
}

  看似代码没有问题,但是一编译就报错,编译器显示"error: ‘else’ without a previous ‘if’",原来是因为FUN函数是一个代码块,然后if(num>0) FUN(num); 就等价于if(num>0) {…}; 这就是在大括号后面打分号了,这样else就缺少if了
  这个时候我们可以用do{…}while(0)来解决这个问题

④递归调用问题

#define NUM (4 + NUM)

  按前面的理解,(4 + NUM)会展开成(4 + (4 + NUM)),然后一直展开下去,直至内存耗尽。但是,预处理器采取的策略是只展开一次。也就是说,NUM只会展开成(4 + NUM),而展开之后NUM的含义就要根据上下文来确定了。

⑤预处宏参数理

  宏参数中若包含另外的宏,那么宏参数在被代入到宏体之前会做一次完全的展开,除非宏体中含有#或##。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值