预处理与宏定义
1、 C语言编译过程
1、预处理: 头文件包含、宏替换、条件编译、删除注释 (不做语法检查)
gcc -E hello.c -o hello.i
2、编译: 将预处理后的文件生成汇编文件 (语法检查)
gcc -S hello.i -o hello.s
3、汇编: 将汇编文件编译成二进制文件
gcc -c hello.s -o hello.o
4、链接:
gcc hello.o -o hello
一步到位: gcc xxx.c -o xxx
gcc可以同时编译多个文件:gcc a.c b.c c.c … -o 可执行文件名(不用加头文件)
参数: -D macro … 编译时指定一个宏,相当于C语言中的 #define macro
2、头文件
防止头文件重复包含
3、宏定义
3.1无参数的宏定义
#define 宏名 字符串
例: #define PI 3.141926
注:
1.在编译预处理时,将程序中在该语句以后出现的所有的PI都调用3.141926代替
2.这种方法使用户能以一个简单的名字代替一个长的字符串
3.在预编译时将宏名替换成字符串的过程称为“宏展开”
4.宏定义,只在宏定义的文件中起作用
终止宏的作用范围:#undef
3.2带参数的宏(宏函数)
#include<stdio.h>
//宏展开 本质就是 替换
#define MY_MUL1(a,b) a*b
#define MY_MUL2(a,b) ((a)*(b))
void test01()
{
printf("%d\n",MY_MUL1(10,20)); //MY_MUL1(10,20) == 10*20
//MY_MUL1( 10+10, 20+20 ) == 10+10*20+20
printf("%d\n",MY_MUL1(10+10,20+20)); //230 不能保证完整性
//MY_MUL2( 10+10, 20+20 ) == ((10+10)*(20+20))
printf("%d\n",MY_MUL2(10+10,20+20)); //800 保证完整性
return;
}
int main(int argc,char *argv[])
{
test01();
return 0;
}
3.3 带参数的宏(宏函数)和普通函数有啥区别
带参数的宏(宏函数):调用多少次就会展开多少次,执行代码的时候没有函数调用过程,也不需要函数的输入栈,所以带参数的宏 浪费了空间,节省了时间。
带参数的函数:代码只有一份,存在代码段中,调用的时候去代码段读取函数指令,调用的时候要压栈(保存调用函数前的相关信息),调用完出栈(恢复调用函数前的相关信息),所以函数浪费了时间节省了空间。
4 条件编译
#include<stdio.h>
#define MY_MAX(a,b) a>b ? a:b
#define MY_MIN(a,b) a<b ? a:b
//字符串大小写转换
void test01()
{
char buf[30]="";
printf("please enter:");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;
int i=0;
while(buf[i]!='\0')
{
#if 1
if(buf[i]>='A' && buf[i]<='Z')
{
buf[i]=buf[i]+32;
}
#else
if(buf[i]>='a' && buf[i]<='z')
{
buf[i]=buf[i]-32;
}
#endif
i++;
}
printf("%s \n",buf);
}
int main()
{
test01();
int ret=MY_MAX(211,985);
printf("%d\n",ret);
return 0;
}
5 关于##
##被称为连接符(concatenator),用来将两个Token连接为一个Token。
注意:这里连接的对象是Token即可,而不一定是宏的变量。
#include<stdio.h>
#define NAME(n) int_name##n
int main()
{
int NAME(a);
int NAME(b);
NAME(a)=211;
NAME(b)=985;
printf("%d\n",NAME(a));
printf("%d\n",NAME(b));
return 0;
}
- int NAME(a); int NAME(b); 在经过预处理后这一声明变为:int int_namea; int int_nameb;
- 可以看出,预编译时,宏参数n被替换,并和name连接成一个整体,然后整体替换NAME宏。