深入理解预处理

目录

1.数值宏常量

2.字符串宏常量

3.用宏定义充当注释符号

4. 用 define 宏定义表达式

5.#undef

6.条件编译

7.文件包含

选学


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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值