目录
1.数值宏常量
从本行宏定义开始,以后的代码就就都认识这个宏了;也可以把任何东西定义成宏。因为编译器会在预编译的时候用真身替换替身,而在我们的代码里面却又用常常用替身来帮忙。看例子: #define PI 3.14159265
2.字符串宏常量
宏定义代表字符串的时候,一定要带上双引号,可以用\续行
#define PATH1 "C:\\Users\\whb\\Desktop\\比特科技\\1-教学服务团队\\1-比特课件\\1.C语\
言课件\\2021C语言深度剖析"
int main()
{
const char *path = PATH1;
printf("%s\n", path);
system("pause");
return 0;
}
3.用宏定义充当注释符号
程序翻译过程:
//预处理-E :头文件展开,去注释,宏替换
//编译-S : 将C语言,编译成为汇编语言
//汇编-c :将汇编翻译成为目标二进制文件
//链接 :将目标二进制文件与相关库链接,形成可执行程序
重点讨论预处理阶段,去注释和宏替换(预处理指令)先后顺序问题
#define BSC //
int main()
{
BSC printf("hello world\n");
return 0;
}
//查看预处理过程
[whb@VM-0-3-centos code]$ gcc -E test.c -o test.i
[whb@VM-0-3-centos code]$ vim test.i
...
# 2 "test.c" 2
int main()
{
printf("hello world\n");
return 0;
}
//倘若,先执行宏替换,那么先得到的代码应该是
int main()
{
//将BSC替换成为‘//’
// printf("hello world\n");
return 0;
}
//再执行去注释,那么代码最终的样子,应该是
int main()
{
return 0; //printf被注释掉
}
int main()
{
BSC printf("hello world\n"); //因为BSC是空,所以在进行替换之后,就是printf("hello world\n");
return 0;
}
结论:预处理期间:先执行去注释,在进行宏替换
4. 用 define 宏定义表达式
最好不要带;在某些情况下可能出现错误
补充内容:宏的名称中不允许有空格,(使用可以)且必须遵守c变量的命名资格:只能使用字符,数字,下划线(—)字符,且首字符不能是数字。预处理器不做计算,不求值,只是替换字符序列,可在替换字符中使用圆括号得到符合预期的乘法运算。
5.#undef
宏的有效范围,是从定义处往下有效,之前无效
void show()
{
#define i 1000
}
int main()
{
printf("%d\n",i);
show();
printf("%d\n", i);//都有效
return 0;
}
undef本质作用
#include <stdio.h>
#define M 10
int main()
{
#define N 100
printf("%d, %d\n", M, N);
printf("%d, %d\n", M, N);
printf("%d, %d\n", M, N);
#undef M //取消M
#undef N //取消N
从该位置起,M,N不在被识别
printf("%d, %d\n", M, N);
printf("%d, %d\n", M, N);
printf("%d, %d\n", M, N);
return 0;
}
结论:undef是取消宏的意思,可以用来限定宏的有效范围。
int main()
{
#define X 3
#define Y X*2
#undef X
#define X 2
int z = Y;
printf("%d\n", z);
return 0;
}
结果为4
6.条件编译
条件编译的功能使得我们可以按不同的条件去编译不同的程序部分,因而产生不同的目 标代码文件。这对于程序的移植和调试是很有用的。
举一个例子吧 我们经常听说过,某某版代码是完全版/精简版,某某版代码是商用版/校园版,某某软件是基础版/扩展版等。 其实这些软件在公司内部都是项目,而项目本质是有多个源文件构成的。所以,所谓的不同版本,本质其实就是功能的有 无,在技术层面上,公司为了好维护,可以维护多种版本,当然,也可以使用条件编译,你想用哪个版本,就使用哪种条件 进行裁剪就行。 著名的Linux内核,功能上,其实也是使用条件编译进行功能裁剪的,来满足不同平台的软件。
概括:
第一种形式:
#ifdef 标识符
程序段 1
#else
程序段 2
#endif
它的功能是,如果标识符已被 #define 命令定义过则对程序段 1 进行编译;否则对程序段 2
进行编译。如果没有程序段 2(它为空),本格式中的#else 可以没有,即可以写为:
#ifdef 标识符
程序段
#endif
第二种形式:
#ifndef 标识符
程序段 1
#else
程序段 2
#endif
与第一种形式的区别是将“ifdef”改为“ifndef”。它的功能是,如果标识符未被#define 命令定
义过则对程序段 1 进行编译,否则对程序段 2 进行编译。这与第一种形式的功能正相反。
前两种没有判断宏的真假,只需定义即可!
第三种形式:
#if 常量表达式
程序段 1
#else
程序段 2
#endif
它的功能是,如常量表达式的值为真(非 0),则对程序段 1 进行编译,否则对程序段 2 进行
编译。因此可以使程序在不同条件下,完成不同的功能。
至于#elif 命令意义与 else if 相同,它形成一个 if else-if 阶梯状语句,可进行多种编译选择
#define ppt
int main()
{
#if ppt
{
printf("hello");//未定义ppt的值,会报错。若不定义ppt,则默认为假,执行else语句。
}
#else
{
printf("other");
}
#endif
return 0;
}
条件编译:对代码进行裁剪,可快速实现某些目的(版本维护,跨平台等.....)
可以通过编译器进行宏定义,不在代码行中定义,了解即可,当然vs也可以进行这种演示。
使用#if可以有很多中写法,列举了几种(与if else语句类似):
#define DEBUG 3
int main()
{
#if DEBUG==0
printf("hello bit 0\n");
#elif DEBUG==1
printf("hello bit 1\n");
#elif DEBUG==2
printf("hello bit 2\n");
#else
printf("hello else\n");
#endif
return 0;
}
#define DEBUG
int main()
{
#if defined(DEBUG)//#if defined()==#ifdef
printf("hello debug\n");
#else
printf("hello release\n");
#endif
return 0;
}//#if !define(DEBUG)相同
#define C
#define CPP
int main()
{
#if defined(C) && defined(CPP)
printf("hello c&&cpp\n");
#else
printf("hello other\n");
#endif
return 0;
}
int main()
{
#if defined(C)
#if defined (CPP)
printf("hello CPP\n");
#endif
printf("hello C\n");
#else
printf("hello other\n");
#endif
return 0;
}
7.文件包含
#ifndef XXX
#define XXX
//TODO
#endif
#include本质是把头文件中相关内容,直接拷贝至源文件中!
结论:所有头文件都必须带上条件编译,防止被重复包含! 那么,重复包含一定报错吗??不会! 重复包含,会引起多次拷贝,主要会影响编译效率!同时,也可能引起一些未定义错误,但是特别少。
选学
//#pragma message()作用:可以用来进行对代码中特定的符号(比如其他宏定义)进行是否存在进行编译时消息提醒
#include <stdio.h>
#define M 10
int main()
{
#ifdef M
#pragma message("M宏已经被定义了")
#endif
system("pause");
return 0;
}
#pragma warning(disable:4996) //禁止4996报错
int main()
{
int x = 10;
scanf("%d", &x);
printf("hello : %d\n", x);
system("pause");
return 0;
}