gpt4 book ai didi

C语言编程之预处理过程与define及条件编译

转载 作者:qq735679552 更新时间:2022-09-27 22:32:09 25 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章C语言编程之预处理过程与define及条件编译由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

这张图描述了从源文件到可执行文件的整体步骤:

C语言编程之预处理过程与define及条件编译

这张图展示了大体上步骤.

C语言编程之预处理过程与define及条件编译

从代码到运行环境,编译器提供了翻译环境。在一个程序中,会存在多个文件 ,而每个源文件都会单独经过编译器处理.

C语言编程之预处理过程与define及条件编译

预编译: 1,会将#include等头文件所包含的内容,库函数全部拷贝过来 2,代码中注释的删除 3,由#define所定义的符号全部替换进代码中 对预处理指令的操作 。

编译:把C代码翻译成汇编代码 1,语法分析(判断是否存在语言的语法错误而造成无法编译) 2,词法分析 3,语义分析(分析每句代码的意思) 4,符号汇总(会将整个程序中的全局符号进行汇总) 。

汇编 1,形成符号表 。

C语言编程之预处理过程与define及条件编译

一个程序,两个文件test.c与add.c,在test.c中,有 。

extern int add(int x,int y);//声明对该函数引用,在其他文件中找该函数。

在汇编时,各个文件都会形参函数符号表,但extern并不会形成地址标记,只是一个0x00.

链接:合并段表 符号表的合并与重定位检查各个函数及其定义声明 。

C语言编程之预处理过程与define及条件编译

  。

名示常量#define

define的预处理指令 。

#define MACRO substitution 预处理指令 宏 替换体 。

宏只是起到替换作用,在替换过程中不产生任何运算 举个例子 。

#define SUM 2+2int num = SUM * SUM;

不了解的人可能会认为是 num=44; 但,实际是替换作用 num=2+22+2; 这就是宏只起到替换作用的意思.

再来介绍一个原理性概念 。

记号 从技术角度来看,可以把宏的替换体看作是记号型字符串。而不是字符型字符串。在C预处理器记号是宏定义的替换体的中单独的“词”,用空白把词分开。例如:

#define FOUR 2*2#define FOURS 2 * 2

这两个对于预处理器要看预处理器把这个替换体看成什么。如果是字符型字符串,这空格也会是字符串的一部分。但如果是记号型字符串,空格就会被认为是分隔符,就和2*2是一个意思。总之要看编译器的规则。 总结一下 。

如果编译器理解替换体是字符型字符串,那么空格就会被认为是字符串的一部分 2 * 2就和2*2不是一个意思。 如果编译器理解为记号型字符串,那么空格就会被认为只是分隔符,并不影响。空格不算替换体的一部分 2 * 2和2乘2则是一个意思.

  。

重定义常量

假设把MAX设为30,在文件中又把它重新定义为10.这个过程叫重定义常量但不同的标准有不同的规则。有一些允许重定义,但是会报警。ANSI标准则采用,只有新旧定义完全相同才允许重定义。 完全相同 意味着替换体中必须记号完全相同,顺序也必须相同 .

#define MAX 2 * 3#define MAX 2 * 3

这才允许 。

#define MAX 2 * 3#define MAX 2*3

这不符合那个标准。(虽然我不知道这个标准的重定义有什么用,我比较菜) 注:根据一个大佬的建议,这类代码非常致命,非常不好,最好不使用.

  。

在#define中使用参数

在#define中也可以创建外形和作用与函数类似的类函数宏。 带有函数的宏可以达到部分函数的作用.

#define	SQUARE(X) X*Xmul=SQUARE(2);

与函数调用有些相似。 同时最好使用足够多的括号去确保运算和结合性的正确.

mul=SQUARE(x++)

则会造成运算不符合要求.

用宏参数创建字符串:#运算符 。

#define PSQRA(X) printf("X is %d\n",((X)*(X));#define PSQRB(X) printf("#X" is %d\n",((X)*(X));

这两个是可以打印出不同的效果 #作为一个预处理运算符,可以把记号转换成字符串,如果X是一个宏形参,那么#X就是“X”的字符串的形参名.

这叫字符串化 。

int y=50;PSQRA(y)X is 2500PSQRB(Y)y is 2500PSQRA(2+4)X is 36PSQRB(2+4)2+4 is 36

这就是区别.

  。

预处理器粘合剂:##运算符

#运算符可以作用于宏的替换体 而##运算符也可以作用.

#define NUMBER(n) X##nNUMBER(4)可展开为x4

例如 。

#include <stdio.h>#define XNAME(N) x##N#define PRINT(N) pritnf("x"#N"=%d\n",x##N);int main(void){	int XNAME(1) = 10;//x1=10	int XNAME(2) = 20;//x2=10	int x3 = 0;	PRINT(1);//printf("x1=%d",x1);	PRINT(2);//printf("x2=%d",x2);	PRINT(3);//printf("x3=%d",x3);}

C语言编程之预处理过程与define及条件编译

  。

变参宏:… 和_ _ VAG_ARGS_ _

一些函数可以接受数量可变的参数(就是没有固定传递的参数的数量,如printf()和scanf())。而宏也可以拥有这样的能力.

#define PR(...) printf(_ _VAG_ARGS_ _)PR("HELLO WORLD");//printf("HELLO WORLD");PR("x1=%d,x2=%d",10,20);//printf(""x1=%d,x2=%d",10,20);

相当于这样的效果。 省略号只能代替最后的宏参数。不能在省略号加其他参数.

#define PR(x,...,y) #x #_ _VAG_ARGS_ _ #y

是不被允许的.

  。

宏与函数

有相当一部分的宏可以起到和函数一样的效果,但到底该怎么选呢? 宏和函数可以达到同样效果。宏比函数要简单一些,同时,在编译器的消耗时间也要远小于函数。但是稍有不慎就会产生一些副作用,导致结果不可预测。 宏与函数的比较实际上就是关于时间与空间的比较.

宏在预编译的时候会生成内联代码,也就是会在程序中替换生成语句。如果调用20次,则会在程序中插入20行代码.

但如果调用函数20次,函数也只有一份副本,节省了相当一部分空间。但执行函数时,要调用,再执行,再返回,远比宏插入内联语句消耗的时间要多.

宏较函数也存在缺陷 。

  • 当宏较大时会增加代码长度。
  • 宏是无法调试(在预编译的时候就已完成替换),可能会出现问题。
  • 宏由于不要求类型,会造成不严谨(这也是对于函数的一个好处,函数传参会要求参数类型,而宏只会将参数当作字符串处理,只要是int或float类型都可以)
  • 宏可能会带来运算符优先级的问题,使运算不可预测。

自己按照情况去使用。如果使用宏容易出现副作用,那还是调用函数吧.

但要记住以下几点 。

1,记住宏名中不允许有空格,但在替换字符串中可以有空格。ANSI C允许在参数列表中使用空格.

2,用括号把宏的参数和替换体括起来,正确展开,防止出现副作用.

3,一般用大写字母表示宏常量,一般不全大写表示宏函数 。

4,如果用宏来加快函数的运行速度,要先确定宏和函数之间是否有差距。且,如果只使用一次那对速度加快影响不大。最好在多重嵌套中使用.

  。

预处理指令

当编译器碰上#include 指令时,会查看后面文件名并把内容添加到当前文件中.

#include <stdio.h>//查找系统目录文件#include "mtfile.h"//查找当前工作目录#include "/usr/biff/file.h"//查找/usr/biff/目录

不同的系统有不同的规则,但<>与“”的规则是不变的.

通过头文件的引用,我们才能使用各种函数。头文件中有各种函数的声明。再通过库文件去调用函数的原型.

  。

#undef指令

可以通过该指令去向之前#define定义的宏 。

#include <stdio.h>#define MAX 10#undef MAXint main(void){	printf("%d", MAX);}

C语言编程之预处理过程与define及条件编译

直接报错 #undef可以取消,某个宏的定义,可以用用来防止某个宏被重复定义,造成错误.

  。

从C预处理器的角度看已定义

预处理器在处理标识符时遵循相同规则。当预处理器发现一个标识符时,会将其当作已定义或未定义。而这里的已定义是由预处理器决定。如果该标识符是由define定义的且没有undef取消,那就是已定义。如果是定义的某个全局变量,那就是未定义(对预处理器而言).

#define定义的宏的作用域从文件开头开始,延申至文件结尾或者遇到#undef取消定义,如果跨文件使用,那使用的位置要在#include引用的文件后.

  。

条件编译

就跟条件判断语句有着类似的意思.

#ifdef , #else , #endif 。

举个例子 。

#include <stdio.h>#define MAX 10#undef MAX#ifdef MAX	#include <string.h>	#define MIN 10#endif // int main(void){	printf("%d", MIN);}

C语言编程之预处理过程与define及条件编译

结果就是这个。 但屏蔽#undef 。

#include <stdio.h>#define MAX 10//#undef MAX#ifdef MAX	#include <string.h>	#define MIN 10#endif // int main(void){	printf("%d", MIN);}

C语言编程之预处理过程与define及条件编译

可以运行。 条件编译指令与条件判断语句类似。 只不过,条件判断语句是判读是否执行, 二条件编译指令是判断是否进行预编译。 #endif用来结束该指令的范围 再引入一个指令 。

#ifndef DEBUG 。

如果DEBUG未定义就执行编译,如果已定义就不执行.

还有这几个指令,非常接近条件判断语句 。

#if ,#elif ,#else 。

#if和#elif与if和else if类似。但它们的后面接整形常量表达式。 0为假,非0为真 都是判断是否进行预编译 。

可以通过条件编译指令去防止某些文件被多次调用导致问题出现 。

#ifndef _FILE_H#define _FILE_H文件内容。。#endif

或者直接使用 。

#pragma once//可以保证文件只是使用一次

  。

offsetof函数

size_t offsetof( structName, memberName );

用于测算结构体成员相对于起始位置的偏移量 。

实现 。

#define OFFSETOF(structName,memberName) (int)&(((struct structName*)0)->memberName)

从0地址处,开始向成员访问再取地址在强转成整形.

以上就是C语言编程之预处理过程与define及条件编译的详细内容,更多关于C语言预处理的资料请关注我其它相关文章! 。

原文链接:https://blog.csdn.net/weixin_52199109/article/details/115048443 。

最后此篇关于C语言编程之预处理过程与define及条件编译的文章就讲到这里了,如果你想了解更多关于C语言编程之预处理过程与define及条件编译的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com