#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
安装的路径去查找
双引号的查找策略
先去自己所在的目录去查找
先去代码所在的路径下查找
如果代码找不到再去库目录底下查找
整体上找的比较慢,查找必须要两步,一定会去库目录
库目录用尖括号,自己定义双引号
‘
’