预处理(下)

#define定义宏

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

宏的声明方式

#define name(parament -list )stuff

name实际上是宏名 这圆括号里面是参数列表 这些参数会替换stuff文本里面去

文本里面只要出现参数就会替换到parament -list 中里面去

其中parament -list是一个由逗号隔开的符号表,它们可能出现在stuff文本中

#include<stdio.h>
#define SS(X) X*X
int main()
{
	int c = SS(5);
	printf("%d\n", c);
	return 0;
}

猜一下这样写的结果是什么

注意宏是要替换的,传过去的5+1相当于5+1*5+1(宏的话是要和括号靠在一起的,就是SQUARE后面不能有个括号,宏里面的参数是到时候是会替换宏体的)

#include<stdio.h>
#define SS(X) X*X
int main()
{
	int c = SS(5+1);
	printf("%d\n", c);
	return 0;
}

怎么才能修改加括号,因为乘法的优先级比加法高,所以我们只要让加法先加行了,所以加括号

最好给宏定义再加一个括号

不然有些情况是得不到想要的结果的

比如我用10*double(3)这样子是不行的

算一算:10*(3)+(3)=33

达不到我们想要的60的结果

宏的替换规则

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

在调用宏时,首先要对参数进行检查,看看是否包含#define定义的符号,如果是,那么首先将他们替换

还没有开始

我们对参数进行替换

接着替换文本插入到宏的文本里面去

宏体内的参数是可以出现其他宏定义的符号的,但是宏是不能递归的,不可能做到自己调用自己

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

这样子是搜索不到的

预处理操作符#和##

#其实可以把参数作为字符串查入到字符串中去

首先我们先来看看下面这个

下面两个字符串可以直接打印吗

当然是可以的,可以这两个字符串相当于合并了成了一个字符串

#include<stdio.h>
int main()
{
	printf("hello word\n");
	printf("hello ""word");
	return 0;
}

现在我们想要用宏实现这个效果

#的作用就是把这个参数转换成对应的字符

#include<stdio.h>
#define dingy(x) printf("the value  "#x"  of %d\n",x) //预定义时就会被替换成主函数传过来的参数
int main()
{
	int a = 10;
	dingy(a);
	int b = 20;
	dingy(b);
	return 0;
}

上面我们打印的整形,如果我们打印的浮点型那么就不一样了

打印不了浮点型、

#include<stdio.h>
#define dingy(x,SAD) printf("the value  "#x"  of"SAD"\n",x) //预定义时就会被替换成主函数传过来的参数
int main()
{
	float a = 10;
	dingy(a,"%lf");
	float b = 20;
	dingy(b,"%lf");
	return 0;
}

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

它允许宏定义从分离的文本片段创建文本标识符

#include<stdio.h>
#define SSS(CLAA,SS) CLAA##SS
int main()
{
	int class106 = 50;
	 int cc=SSS(class, 106);//把他们两个的变量合并,然后就等于class106,这个的值是50
	printf("%d\n",cc);
	return 0;
}

如果写宏的话,传过去带有副作用的参数,那么就会发生不可预知的危险

第一种是不带副作用的,a的话是没有任何影响的m。

第二种是带副作用的,a的话在赋值的时候是会改变值的,所以这就是有带有副作用的参数

如果我们写了一个宏定义带副作用的表达式会这么样

#include<stdio.h>
#define SS(a,b) ((a)>(b)?(a):(b))
int main() 
{
	int a = 5;
	int b = 4;
	printf("%d\n",SS(a++,b++));//首先先把值传过去替换成(5++)>(4++)?(5++):(4++)  5和4先比较,5赢,然后加加变成6 就输出6
	printf("%d   %d",a,b);	//a经过两次加加变成7,b++变成5
	return 0;
}

宏和函数

函数需要函数调用(函数传参,栈帧创建) 计算,函数返回

真正计算才算0.1秒,函数调用和函数返回就用了0.2秒了,总共0.3秒

宏的话就省去了函数调用和函数返回的时间

函数和宏的比较

我们小型的计算宏就比较好

宏的话是没办法调试的,宏的话在预编译的时候就已经是替换完了,你根本看不到他调试

宏的话没法调试。没有类型限制,那么就不严谨

函数能传参数,但不能传类型,但是宏的话什么都能传

函数是做不到传类型的

#include<stdio.h>
#include<malloc.h>
#define MALLOC(TEY,ER) (ER*)(malloc(TEY*(sizeof(ER))))
int main()
{
	malloc(40);//开辟10个整形的空间
	//malloc(10,int)//但是我想要表示是10个整形,我要让别人看到,这样就不行。这时候就要用到宏
	int *p=MALLOC(10,int);

	return 0;
}

函数和宏的比较

写宏的话一般用大写,函数一般用小写

offsetof这是个宏,虽然他们是小写

getchar有时是宏有时是函数

宏定义也是可以取消掉的

#undef

这样子程序会报错,说你的宏为定义

#include<stdio.h>
#include<malloc.h>
#define MALLOC(TEY,ER) (ER*)(malloc(TEY*(sizeof(ER))))
int main()
{
	malloc(40);//开辟10个整形的空间
	//malloc(10,int)//但是我想要表示是10个整形,我要让别人看到,这样就不行。这时候就要用到宏
	#undef MALLOC;
	int *p=MALLOC(10,int);
	//int *w= (int*)(malloc(10 * (sizeof(int))))    //这个就是宏预编译转换的
	return 0;
}

条件编译

条件满足就编译,条件不满足就不编译

看_DEBUG_有没有定义,定义就打印,不定义就不打印

不想打印的话把这样代码注释掉就行了

#if条件编译符号,如果if后面的值为真就参与编译否者就不编译(就是不参与编译在预处理阶段就直接把这句话删了)

上面这两个条件编译指令,是一队前后要呼应的

#if后面为真就参与编译,为假就不编译(后面不能是变量)

#include<stdio.h>
int main()
{
#if 0
	printf("可以");
#endif
}

#include<stdio.h>
#define M 5
int main()
{
#if M<6
	printf("可以");
#elif M>3
	printf("不可以");
#else
	printf("真的不可以");
#endif

}

还是判断宏有没有定义,有就为真,没有就为假

实操

#include<stdio.h>
#define M 5
int main()
{
#if defined(M)//判断有没有定义有定义的话就为真可以输出
	printf("可以");
#endif
	return 0;
}
#include<stdio.h>

int main()
{
#if !defined(M)//判断没有定义的话就为真可以输出
	printf("可以");
#endif
	return 0;
}

另外一种写法

#include<stdio.h>
#define M 5
int main()
{
#ifdef M//判断有没有定义,有的话的话就为真可以输出
	printf("可以");
#endif
	return 0;
}

如果没定义也可以参与编译呢

#include<stdio.h>

int main()
{
#ifndef M//判断没有定义,有的话的话就为真可以输出
	printf("可以");
#endif
	return 0;
}

文件包含

头文件包含多次,我可能在预处理test.i的文件添加很多行重复的代码

像我们平常不是见到这个吗#ifndef表示未定义为真

第一次我们没有定义所以为真继续执行,第二次就包含就为假,就不会执行

这样写可以重复多次的包含

这样子也可以,头文件只会包含一次

包含头文件尖括号和引号是查找的策略不一样

如果是尖括号

查找策略,直接去库目录下查找

编译器提供的库函数的查找

对于vs

安装的路径去查找

双引号的查找策略

先去自己所在的目录去查找

先去代码所在的路径下查找

如果代码找不到再去库目录底下查找

整体上找的比较慢,查找必须要两步,一定会去库目录

库目录用尖括号,自己定义双引号

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值