【C语言终章】预处理详解(上)

【C语言终章】预处理详解(上)

  当你看到了这里时,首先要恭喜你!因为这里就是C语言的最后一站了,你的编程大能旅途也将从此站开始,为坚持不懈的你鼓个掌吧!

🥕个人主页:开敲🍉

🔥所属专栏:C语言🍓

🌼文章目录🌼

1. 预定义符号

2. #define定义常量

3. #denfine定义宏

4. 带用副作用的宏参数

5. 宏替换的规则

6. 宏和函数的对比

7. #和##

    7.1 #运算符

    7.2 ## 运算符

1. 预定义符号

  在C语言中的预处理阶段,会处理预定义符号,而C语言中设置了一些预定义符号,我们可以直接使用:

1   __FILE__    //当前文件的路径

2  __LINE__    //当前的行号

3  __DATE__    //当前程序编译瞬间的日期

4  __TIME__    //当前程序编译瞬间的时间

5  __STDC__   //如果编译器遵循ANSI C,其值为1,否则未定义

举个例子:

  在VS编译器中就不遵循ANSI C,因此报错。

2. #define定义常量

#define  name //名称 stuff //定义后的值  

例如:

1  #define  MAX   100    //定义MAX为100

2  #define reg  register   //为 register 这个关键字创建一个简短的名字

3  #define  do_forever  for(;;)   //为死循环创建一个名字

4  #define  CASE break;case      //在写case语句时自动把 break 加上

//如果define定义的语句过长,可以用续行符分成几行写

5  #define  DEBUG_PRINTF     printf("file :%s\tline:%d\t\

                                                     date:%s\ttime::%s\n",\

                                                     __FILE__,__LINE__,   \  //续行符

                                                     __DATE__,__TIME__)

问:在#define定义标识符时,后续需不需要加上;?

答:加上纯属多余,有时还会带来不好的后果

  例如:

这里我们在 100 加上了 ; ,在打印时直接报错了,这是为什么?

  因为MAX 是100;因此,在printf中它是这样的:

1  printf("%d\n",100;);

  在里面多了一个;这显然是错误的语法。

3. #denfine定义宏

  #define机制包括了一个规定,允许把参数直接原样替换到文本中,这种实现通常就称之为宏(macro)或者定义宏(define macro)。

  下面是宏的声明方式:

1  #define name(parament-list)  stuff   //parament-list   参数列表

  注:括号必须和name紧挨着,如果中间有空格,参数列表就会被视为stuff的一部分

举个例子:

1  #define  SQUARE(x)  x*x 

这个宏接收一个参数x,如果你在程序中写下 SQUARE(5),那么这个宏最终就会被预处理器给替换为  5*5

  注:这个宏存在一个问题,下面来看一段代码

  

根据我们以往的知识,这段代码最后输出的应当是 36,因为在函数中,这里传过去的值应当是6,然后计算的就是6*6。下面来看输出结果:

  可以看到,这里输出的居然是 11 ,这是为什么呢?原因就是上面说的原样替换

  像这样原样替换进去后,表达式就变成了 5+1*5+1,因此计算的结果就是11,那如何解决这个问题呢?很简单,只需要在定义时加上括号即可:

1  #define  SQUARE(x)  (x)*(x)

这里还有一个宏定义:

1  #define  ADD(x)  (x)+(x)

这个宏定义也是有着和上面一样的问题:

这里我们本意是想输出120,但这里却输出了66,还是原样替换的原因:

4. 带用副作用的宏参数

  当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。

  1  x+1;    //并没有改变x本身的值,不带有副作用

  2  x++;   //改变了x本身的值,带有副作用

  下面来看一段代码:

  思考一下上面的代码输出的是什么。

  

可以看到,最后输出时,a和b的值都被改变了,这也是上面所说的原样替换:

  

5. 宏替换的规则

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

①  在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,他们首先被替换,例如:

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

③  再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,那么就重复上述过程

  例如:

  注意:

①  宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏,不能,因为宏无法递归。

②  当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索,例如:

6. 宏和函数的对比

  宏通常被用于执行简单的运算,比如在两个数中找出较大的数就可以用下面的宏:

1  #define  MAX(a,b)  (a)>(b)?(a):(b)

  那为什么不用函数来完成这个任务呢?

有两点原因:

①  用于调用函数和从函数返回的代码可能比实际执行这个小型计算机工作所需要的时间更多。所以宏在执行简单运算时在规模和速度方面都要优于函数。

 ②  更为重要的是函数的参数不能以类型的形式传递,所以函数只适合在类型合适的表达式上用。但是宏可以,因为宏所做到的是原样替换,因此在传参是宏并不会在乎你传的是什么,它会直接把你传的参数给替换进宏中,例如:

  前面说了和函数相比宏的优势,接下来讲讲和函数相比宏的劣势:

①  每次使用宏的时候,一份宏定义的代码将插入到程序中。除非定义的宏比较短,否则可能大幅增加程序的长度。

②  由于宏只是一条代码指令,一次性就完成了好几条甚至更多的代码指令,因此,宏是没法进行调试的。

③  宏由于类型无关,因此也就不够严谨

④  宏可能会带来运算符优先级的问题(比如 5.宏替换规则 中的),导致程序的可能会出错。

7. #和##
    7.1 #运算符

  #运算符将宏的一个参数转换为字符串字面量注:它仅允许出现在带参数的宏的替换列表中。

  #运算符所执行的操作可以理解为 "字符串化"。

  下面来看一段代码:

当我们想用宏来实现这一段代码时应该如何做到呢?

我们可能会想到这种方法:

1  #define PRINT(a)  printf("the value of a is %d\n",a)

显然,上面这种方法是行不通的,因为预处理器不会扫描字符串常量中的内容,因此,printf中的a并不会被替换,那么这时候就需要我们用到#运算符了:

这里使用#运算符将a字符串化,变为字符串字面量,这也预处理器就可以扫描a并将其替换。

    7.2 ## 运算符

  ##运算符可以把位于它两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符。##被称为记号粘合。

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

  这里我们想想,在我们想要求两个数中的较大值时,使用函数的话,比较不同类型的数就得实现不同的函数,比如:

  显然,这样写起来太麻烦了,现在我们学习了宏定义之后,可以试着写一下宏:

  在实际的开发过程中##使用的很少,因此很难举例出形象贴切的例子。

                                                创作不易,点个赞呗,蟹蟹啦~

    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/783568.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

04-MySQL数据库-权限管理

一、查看权限 1,查看系统所有权限 mysql> show privileges; 权限字段介绍 privileges #权限名称 context #对象,表示可以对数据库,那些资源、进行哪些操作; comment #描述,备注解释说明; Grant…

Caddy之静态站点应用场景

一、背景与介绍 无意之中看到公司部门的软件介质下载站点不是使用Nginx部署,而是使用Caddy。就比较好奇了,这个Caddy是个什么东西? 为啥他们没用Nginx呢,带着好奇心搜索了一下相关资料。 官方解释: Caddy is a powerful, extensible platfo…

Redis 事务 与 管道

redis事务 谈到事务大家可能就会想起mysql中的事务 注意这里的事务不是指的是事务的四大特性acid 持久性 原子性 隔离性 一致性 事务的概念就是 一组命令,串行化执行而不被打断 这里redis的事务和mysql的事务就不太一样 传统关系型数据库的事务主要强调的是一个没有执行完成就…

neo4j使用详解(六、cypher常用函数语法——最全参考)

Neo4j系列导航: neo4j及简单实践 cypher语法基础 cypher插入语法 cypher插入语法 cypher查询语法 cypher通用语法 cypher函数语法 4.常用函数 主要包括谓词函数(断言函数)、标量函数、聚合函数、字符串函数以及集合函数 4.1.谓词函数&#…

数据结构--循环链表(C语言实现)

一.循环链表的设计 typedef struct CNode{ int data; struct CNode* next; }CNode ,*CList; 2.循环链表的示意图: 3.循环链表和单链表的区别: 唯一区别,没有空指针,尾节点的后继为头,为循环之意. 二.循环链表的实现 //初始化return true; }//返回key的前驱地址,如果…

Lazarus远控组件NukeSped分析

静态信息: 样本md5:9b656f5d7e679b94e7b91fc3c4f313e4 由此可见为假的Adobe Flash Player 的攻击样本 样本分析 通过五个函数,内部调用sub_40159D函数动态获取API函数 利用IDA python解密字符串。。 完整python代码 Python> idc.get_…

MongoDB副本集环境搭建(以单机Windows为例)

前言 近期有搭建MongoDB副本集的需求,简单记录一下搭建过程(以本地Windows环境为例)。 一、副本集选型 1 Primary节点、1 Secondary 节点、1 Arbiter节点模式副本集环境搭建。 二、搭建过程 1. 安装MongoDB服务 下载地址:https://www.mongodb.com,如下图所示: 选择…

基于Springboot旅游网站管理系统设计和实现

基于Springboot旅游网站管理系统设计和实现 博主介绍:多年java开发经验,专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系…

7.卷积神经网络与计算机视觉

计算机视觉是一门研究如何使计算机识别图片的学科,也是深度学习的主要应用领域之一。 在众多深度模型中,卷积神经网络“独领风骚”,已经被称为计算机视觉的主要研究根据之一。 一、卷积神经网络的基本思想 卷积神经网络最初由 Yann LeCun&a…

UE4_碰撞_自定义碰撞检测通道

效果如图: 1、项目设置中新建追踪检测通道weapon,默认值为忽略。 2、新建几个actor作为枪,碰撞预设全部设为自定义,把新建的检测响应weapon设为阻挡。 3、角色进行射线检测 运行效果如下: 发现有些物体碰不到&#xff…

GetSystemTimes:获取CPU占用率(WIN API)

原文链接:https://blog.csdn.net/qq_28742901/article/details/104960653 GetSystemTimes函数: BOOL WINAPI GetSystemTimes(__out_opt LPFILETIME lpIdleTime, // 空闲时间__out_opt LPFILETIME lpKernelTime, // 内核进程占用时间__out_opt LPFILETI…

蓝桥杯 本质上升序列

题目描述: 小蓝特别喜欢单调递增的事物。 在一个字符串中,如果取出若干个字符,将这些字符按照在字符串中的顺序排列后是单调递增的,则成为这个字符串中的一个单调递增子序列。 例如,在字符串 lanqiao 中,如果取出字符…

模型训练----将pth模型转换为onnx

目录 1 安装需要的环境2、模型转换3、测试onnx模型 Github代码 1 安装需要的环境 需要在虚拟环境中安装onnx和onnxruntime(GPU),环境和自己的cuda版本要对应上查询链接 激活环境,查看环境的cuda版本,我是cuda11.6 cudnn8302&a…

AI预测福彩3D第22弹【2024年3月31日预测--第5套算法开始计算第4次测试】

今天,咱们继续进行本套算法的测试,今天为第四次测试,仍旧是采用冷温热趋势结合AI模型进行预测。好了,废话不多说了。直接上结果~ 仍旧是分为两个方案,1大1小。 经过人工神经网络计算并进行权重赋值打分后,3…

开源翻译大模型

开源翻译大模型 1 简介 在开发过程中,会遇到定制化翻译工具的需要,开源的翻译模型可以解决相应的问题。其中英语转中文的比较好的开源项目有: 序号组织模型地址备注1赫尔辛基大学语言技术研究小组(Language Technology Researc…

如何使用Axure RP制作网页原型并结合IIS服务实现公网访问本地HTML网页

文章目录 前言1.在AxureRP中生成HTML文件2.配置IIS服务3.添加防火墙安全策略4.使用cpolar内网穿透实现公网访问4.1 登录cpolar web ui管理界面4.2 启动website隧道4.3 获取公网URL地址4.4. 公网远程访问内网web站点4.5 配置固定二级子域名公网访问内网web站点4.5.1创建一条固定…

SSH免密登录——linux

SSH免密登录——linux 方法一一、用 ssh-key-gen 在本地主机上创建公钥和密钥二、用 ssh-copy-id 把客户端公钥追加到远程主机的 .ssh/authorized_key 上三、直接登录远程主机 方法二一、将生成的客户端公钥id_rsa.pub内容追加至目标主机.ssh/authorized_key 中参考链接 SSH免密…

动态规划-----背包类问题(0-1背包与完全背包)详解

目录 什么是背包问题? 动态规划问题的一般解决办法: 0-1背包问题: 0 - 1背包类问题 分割等和子集: 完全背包问题: 完全背包类问题 零钱兑换II: 什么是背包问题? 背包问题(Knapsack problem)是一种…

日历插件fullcalendar【笔记】

日历插件fullcalendar【笔记】 前言版权开源推荐日历插件fullcalendar一、下载二、初次使用日历界面示例-添加事件,删除事件 三、汉化四、动态数据五、前后端交互1.环境搭建-前端搭建2.环境搭建-后端搭建3.代码编写-前端代码fullcalendar.htmlfullcalendar.js 4.代码…

【更新】在湘源7、8中使用2023年11月国空用地用海分类

之前为了做控规,从湘源8中扒了一套国空用地用海的绘图参数给湘源7使用。 【预告】在湘源控规7中使用 国空用地用海分类标准 但是部里在2023年11月又发布了一套新的用地用海分类。 本想去湘源8里面再扒一下,结果发现湘源8自己还没有更新呢,…