当前位置: 首页 > news >正文

34.0、C语言——C语言预处理(2) - 预编译(预处理)详解(2)

34.0、C语言——C语言预处理(2) - 预编译(预处理)详解(2)

预处理详解:

预定义符号:

        __FILE__                // 进行编译的源文件;

        __LINE__                // 文件当前的行号;

        __DATE__               // 文件被编译的日期;

        __TIME__                // 文件被编译的时间;

        __STDC__               // 如果编译器遵循 ANSI C,其值为1 ,否则未定义 ;经测试VS编译器中这个是未定义的;

这些预定义符号都是C语言内置的,举个例子:

printf("file:%s line:%d\n",__FILE__,__LINE__);

预处理指令:

# 开头的都叫做预处理指令,比如说 -> 

        1. #include

        2. #define

        3. #pragma

        4. #if

        5. #endif

        6. ifdef

        7. #line 

        8. .........

#define 定义标识符

        语法:#define name stuff   【我们在定义#define标识符的时候用的都是大写,这是我们的约定】

实例代码:

#define MAX 100;
#define STR "澜色海湾"

extern int add(int x , int y);
int main() {
	int max = MAX;
	char str[] = STR;
	printf("%d\n",max);
	printf("%s\n",str);
	return 0;
}

        #define 后面可以是 标识符名 也可以是 一段代码,无论是什么,都会在预处理阶段将代码中的相应的 标识符 进行替换;  

问题:在 #define后面需要加 分号;吗?

        不需要,因为在#define 后面加上分号,当预处理去替换的时候会把 分号 一起替换过去,有可能会出现一些问题;

#define 定义宏

        #define 机制包括一个规定,允许把参数替换到文本中,这种实现通常称为宏 ( macro ) 或定义宏 ( define macro );

        下面是宏的声明方式:【定义宏的时候都是大写,这是我们的约定】

        #define name ( parament - list ) stuff 其中的 parament - list 是一个由逗号隔开的符号表,他们可能出现在 stuff 中;

        注意:参数列表的左括号必须与name紧邻;如果两者之间有任何空白、空格存在,参数列表就会被解释为 stuff 的一部分;         

        如:

#define SQUARE(x) x * x //这里指的是会将 SQUARE(x) 替换成 x 的平方

这个宏接收一个参数 x,如果在上述声明之后,你把

SQUARE ( 5 );

置于程序中,预处理器就会用下面这个表达式替换上面的表达式 ->

5 * 5

警告:这个宏存在一个问题 ,观察下面的代码段:

#define SQUARE(x) x*x

int main() {
	int ret = SQUARE(5+1);
	printf("%d",ret);
	return 0;
}

        咋一看,我们可能会认为会输出 36 ,因为 6 的平方等于 36;
        但是实际上输出的是 11;

为什么呢?

        因为这里不是传参【 传参应该是 int x 而不是单单一个 x 】,而是替换,他会完完全全把 x 替换成  5+1,所以计算的是 5 + 1 * 5 + 1 根据计算优先级得出结果为 11;

#define的替换规则:

在程序中扩展 #define 定义符号和宏时,需要涉及几个步骤?

        1. 在调用宏时,首先对参数进行检查,看看是否包含任何由 #define 定义的符号;如果是,他们首先被替换;【例如:DOUBLE(MAX+MAX),此时DOUBLE和MAX都是宏,那么由于MAX是DOUBLE的参数,所以先将MAX替换,再去替换DOUBLE】;

        2. 替换文本随后被插入到程序中原来文本的位置;对于宏,参数名被他们的值替换;

        3. 最后,再次对结果文件进行扫描,看看他是否包含任何由 #define 定义的符号;如果是,就重复上述处理过程;

注意:

        1. 宏参数和#define定义中可以出现其他 #define 定义的变量;但是对于宏,不能出现递归;

        2. 当预处理器搜索 #define 定义的符号的时候,字符串常量的内容并不被搜索【就是字符串常量不会被宏替换掉 】;例如->

#define MAX 100;
int main() {
    printf("MAX = %d" , MAX);
    return 0;
}

        printf() 中的第一个MAX不会被替换,第二个会被宏替换掉;
 

接下看两个比较奇怪的东西:

预处理操作符 # 和 ##

先来看看预处理操作符 #

        如何把宏的参数插入到字符串中?

实例代码如下所示 ->

#define PRINT(x) printf("the value of "#x"is %d\n",x);
int main() {
    int a = 10;
    int b = 20;
    PRINT(a);
    PRINT(b);
    return 0;
}

         此时,PRINT(a); 会被替换成 printf ( " the value of " "a" " is %d\n " , a );      #x 并没有把 a 替换成 10,而是替换成了字符串 "a"; 

        输出的结果就是   the value of a is 10
                                    the value of b is 10   

预处理操作符 ## 又是什么呢? 

## 的作用 ->

## 可以把位于它两边的符号合成一个符号;他允许宏定义从分离的文本片段创建标识符;

举个例子 ->

#define CAT(X,Y) X##Y

int main() {
    int lanSeHaiWan = 100;
    printf("%d\n",CAT(lanSe,HaiWan));
    return 0;
}

        执行代码后,这段代码中的:

printf("%d\n",CAT(lanSe,HaiWan));

        会被替换成 ->

printf("%d\n",lanSe##HaiWan);
//由于 ## 会把两端的符号合并成为一个,其实就相当于是 ->
printf("%d\n",lanSeHaiWan);

        所以输出的结果依旧是 100

注:这样的连接必须产生一个合法的标识符;否则结果就是未定义的;

相关文章:

  • ES优化实战 - 小操作节省百分之三十以上的磁盘空间
  • [Go WebSocket] 多房间的聊天室(五)用多个小锁代替大锁,提高效率
  • 我在windows环境下的YOLOV3环境搭建过程
  • bat goto 还是 call
  • JVM垃圾回收系列之垃圾收集算法
  • 计算机毕业设计选题推荐 40个高质量计算机毕设项目分享【源码+论文】(三)
  • BDD - SpecFlow BDD 测试实践 SpecFlow + MSTest
  • CRM项目记录(四)
  • React组件的生命周期函数
  • FFmpeg源码分析:avformat_open_input()打开媒体流
  • 深入理解关键字 一(auto,register,static,sizeof)
  • 基于Springboot+vue的停车场管理系统(Java毕业设计)
  • 详解CAN总线:CAN总线报文格式—数据帧
  • mysql进阶:canal实现mysql数据同步到redis|实现自定义canal客户端
  • React路由三种渲染方式、withRouter高阶组件、自定义导航组件
  • $translatePartialLoader加载失败及解决方式
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • 【跃迁之路】【519天】程序员高效学习方法论探索系列(实验阶段276-2018.07.09)...
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • Linux gpio口使用方法
  • Markdown 语法简单说明
  • miniui datagrid 的客户端分页解决方案 - CS结合
  • MySQL QA
  • Node 版本管理
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • 第2章 网络文档
  • 构造函数(constructor)与原型链(prototype)关系
  • 基于 Ueditor 的现代化编辑器 Neditor 1.5.4 发布
  • 浅谈JavaScript的面向对象和它的封装、继承、多态
  • 使用 Xcode 的 Target 区分开发和生产环境
  • MiKTeX could not find the script engine ‘perl.exe‘ which is required to execute ‘latexmk‘.
  • 继 XDL 之后,阿里妈妈开源大规模分布式图表征学习框架 Euler ...
  • ​香农与信息论三大定律
  • #etcd#安装时出错
  • #pragma once与条件编译
  • (6)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—使用Adaboost建模及工作环境下的数据分析整理
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (TOJ2804)Even? Odd?
  • (第二周)效能测试
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (附源码)ssm基于jsp高校选课系统 毕业设计 291627
  • (转)EOS中账户、钱包和密钥的关系
  • (转)socket Aio demo
  • .bat批处理(八):各种形式的变量%0、%i、%%i、var、%var%、!var!的含义和区别
  • /etc/skel 目录作用
  • ??eclipse的安装配置问题!??
  • @Autowired和@Resource装配
  • [ vulhub漏洞复现篇 ] Apache Flink目录遍历(CVE-2020-17519)
  • [ 英语 ] 马斯克抱水槽“入主”推特总部中那句 Let that sink in 到底是什么梗?
  • [asp.net core]project.json(2)
  • [C#]获取指定文件夹下的所有文件名(递归)
  • [C#7] 1.Tuples(元组)
  • [c#基础]值类型和引用类型的Equals,==的区别