C陷阱与缺陷——第6章 预处理器

在严格意义上的编译过程开始之前,C语言预处理器首先对程序代码做了必要的转换处理。预处理器的主要作用是:

  • 我们有时需要将某个特定数量在程序中出现的所有实例统统加以修改
  • 大多数C语言实现在函数调用时都会带来重大的系统开销

1. 不能忽视宏定义中的空格

#define f (x) ((x)-1)

此时f(x)代表:

(x)((x)-1)

如果希望定义f(x)为((x)-1),必须这样写:

#define f(x) ((x)-1)

空格的规则适用于宏定义,但是不适用于宏调用,所以f(3)和f (3)的结果都等于2

2. 宏并不是函数

宏定义中出现的所有括号,它们的作用时预防引起与优先级有关的问题,如果没有括号求绝对值的定义如下:

#define abs(x) x>0?x:-x

此时abs(a-b)求值的结果将是

a-b>0?a-b:-a-b

-a-b与我们期望的-(a-b)是不一样的。

正确的实现应该是:

#define abs(x) ((x)>=0)?(x):-(x))

即使宏定义中的各个参数与整个结果表达式都被括号括起来,也仍然还可能有其他问题存在,比如说,一个操作数如果在两处被用到,就会被求值两次。比如:

biggest=max(biggest, x[i++]);

在宏展开后将会变成:

biggest=((biggest)>(x[i++])?(biggest):(x[i++]));

i++可能被执行两次,导致错误,解决这个问题的办法:

  • 确保max中参数没有像++,--这样的副作用
  • 将max使用函数实现

使用宏的另外一个危险是:宏展开可能产生非常庞大的表达式,占用空间远远超过编程者所期望的空间,比如:

3. 宏并不是语句

assert宏的一个错误实现:

#define assert(e) if (!e) assert_error(_FILE_, LINE_)

但这样会导致一些错误:

这个修复方式是把宏体整个括起来,即如下:

#define assert(e)\{if (!e) assert_error(__FILE__, __LINE__);}

但是这样会带来新的问题,上面的例子展开后如下:

if(x > 0 && y > 0){if(!(x > y)) assert_error("foo.c", 37);};
else{if(!(y > x)) assert_error("foo.c", 39);};

在else之前的分号是一个语法错误。因此assert宏的正确实现如下:

#define assert(e) \((void)((e)||_assert_error(__FILE__,__LINE__)))

另外一个实现如下:

#define assert(expression) \((void)((expression) || \(printf("Assertion failed: %s, file %s, line %d\n", \
#expression, __FILE__, __LINE__), abort(), 0)))

4. 宏并不是类型定义

宏的一个常见用途是使多个不同变量的类型可在一个地方说明,宏定义的这种用法有一个优点——可移植性。但是用宏定义类型会有一些问题,还是推荐使用typedef关键字:

#define T1 struct foo *
typedef struct foo *T2T1 a,b;
T2 a,b;

其中T1 a,b;将被扩展为:

struct foo * a, b;

这个语句中a被定义为一个指向结构的指针,而b却被定义为一个结构,而不是指针。

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

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

相关文章

Kontakt v7.7.2(音频采样器)

Native Instruments Kontakt 7是一款强大的软件采样器,它允许用户从各种来源采样音频并进行编辑和处理。它包含大量预设采样库,包括乐器、合成器、鼓组和声音效果等。此外,Kontakt 7还允许用户创建自己的采样库,以便根据自己的需要…

电脑版的便签软件使用哪一款?

您会选择使用电脑便签软件吗?很多人在日常工作及生活中会选择用电脑便签来督促自己按时完成工作任务,而且选择电脑便签是一个难题,毕竟当前电脑便签类工具非常多,如果想要找一款功能比较强大且又简单易用的便签工具,那…

HarmonyOS学习--创建和运行Hello World

创建和运行Hello World 打开DevEco Studio,在欢迎页单击Create Project,创建一个新工程。根据工程创建向导,选择创建Application应用或Atomic Service元服务。选择“Empty Ability”模板,然后单击Next。关于工程模板的介绍和支持…

A股股票交易费用

A股股票交易费用如下: 经手费:(上证/深证)按成交额双边收取0.0487‰,交给交易所。 监管费:(深证)按成交额双边收取0.02‰,交易所代收,交给证监会。 证管费&…

QGraphicsView实现简易地图7『异步加载-多瓦片-无底图』

前文链接:QGraphicsView实现简易地图6『异步加载-单瓦片-无底图』 前一篇文章提到的异步单瓦片加载,是指线程每准备好一个瓦片数据后,立刻抛出信号让主线程加载。而本篇异步多瓦片加载是指线程准备好所有瓦片数据后,一起抛出信号让…

将文件夹中所有文件名取出

dir C:\Users\是啊\Desktop\实验五/b>C:\Users\是啊\Desktop\1111.xls C:\Users\是啊\Desktop\实验五(这个是文件夹路径) /b (参数) C:\Users\是啊\Desktop\1111.xls(文件名输出的文件路径)

用VR+科普点亮科技之光VR航天科普体验巡展

11月22日至26日,第十一届中国(绵阳)科技城国际科技博览会圆满闭幕。本届科博会以“科技引领创新转化开放合作”为主题,创新办展办会模式,搭建高能级科技合作交流平台,展示了国内外科技创新发展成就和最新成果,举办了多…

内核无锁队列kfifo

文章目录 1、抛砖引玉2、内核无锁队列kfifo2.1 kfifo结构2.2 kfifo分配内存2.3 kfifo初始化2.4 kfifo释放2.5 kfifo入队列2.6 kfifo出队列2.7 kfifo的判空和判满2.8 关于内存屏障 1、抛砖引玉 昨天遇到这样一个问题,有多个生产者,多个消费者&#xff0c…

【从零认识ECS云服务器 | 快速上线个人网站】二、使用ECS云服务器

第二章 使用ECS 2.1 获取ECS 方式一:通过试用中心免费领取ECS实例 满足以下全部条件的阿里云用户,可免费试用云服务器ECS: 阿里云注册会员用户并完成阿里云企业认证或个人认证用户。申请用户是云服务器ECS产品的新用户,可以申…

查询不用order by时结果默认怎么排序

总结: 如果在使用没有指定order by,那么基本上依赖于底层实现的,具体排序规则不定,所以排序的顺序也不固定,可能会随着时间发生变化。 在实际工作中,如果有查询列表展示数据的功能和需求,开发前一定要先确…

Linux 进程地址空间

文章目录 进程地址空间进程地址空间结构页表虚拟内存写时拷贝 进程地址空间 进程地址空间难以定义,因为它更像是一个中间件。 程序从磁盘中加载到内存,程序的执行需要硬件资源,所以每个程序启动时会创建至少一条进程,进程作为组…

HarmonyOS 修改App的默认加载的界面(ArkTS版本)(十七)

根据鸿蒙系统APP的应用生命周期结构(鸿蒙4.0开发笔记之ArkTS语法基础之应用生命周期)来看。 1、首先在roject/entry/src/main/ets/entryability/EntryAbility.ts文件中找到UI加载函数:onWindowStageCreate(…){…},然后找到windo…

力扣100 相同的数(两种解法)

给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。 示例 1: 输入:p [1,2,3], q [1,2,3] 输出:true 示例 2&…

【数据分享】11个城市的出租车(网约车)数据(免费获取)

出租车(网约车)GPS数据是我们最常使用的交通大数据之一,但是出租车(网约车)GPS数据没有公开的获取渠道,有些学者可能能通过与相关机构合作拿到数据,但是对于绝大多数普通人是没有这个机会的&…

【理解ARM架构】中断处理 | CPU模式

🐱作者:一只大喵咪1201 🐱专栏:《理解ARM架构》 🔥格言:你只管努力,剩下的交给时间! 目录 🍜中断🍨GPIO中断代码实现 🍜CPU🍨CONTROL…

2024王道考研计算机组成原理——存储系统

微信打开的时候会有一个人站在地球上,这个过程就是把程序从辅存转移到主存,数据只有调入主存当中才可以被CPU访问 cache:主存速度还是慢,为了进一步缓解CPU和主存之间的速度矛盾 在微信打视频聊天的时候,在这一段比较…

弘扬中华文化 感受戏曲魅力——安徽演艺小分队赴和田交流演出

为进一步弘扬中华优秀传统文化,促进皖和两地交往交流交融,12月2日,安徽省演艺小分队走进和田新夜市登台演出,黄梅戏、独唱、民乐演奏、杂技等丰富多样的表演,为观众们送上了一场文化盛宴。 安徽演艺小分队赴和田交流演…

一篇文章带你详细了解C++智能指针

一篇文章带你详细了解C智能指针 为什么要有智能指针内存泄漏1.什么是内存泄漏,它的危害是什么2.内存泄漏的分类3.如何避免内存泄漏 智能指针的使用及原理1.RAII2.智能指针的原理3.auto_ptr4.unique_ptr5.shared_ptr6.weak_ptr 为什么要有智能指针 C引入智能指针的主…

WindowsServer服务器系列:定时备份 MySQL

一、编写脚本 echo 取日期、时间变量值 set yy%date:~0,4% set mm%date:~5,2% set dd%date:~8,2% if /i %time:~0,2% lss 10 set hh0%time:~1,1% if /i %time:~0,2% geq 10 set hh%time:~0,2% set mn%time:~3,2% set ss%time:~6,2% set date%yy%%mm%%dd% set time%hh%%mn%%ss…

Vue2中v-html引发的安全问题

前言:v-html指令 1.作用:向指定节点中渲染包含html结构的内容。 2.与插值语法的区别: (1).v-html会替换掉节点中所有的内容,{{xx}}则不会。 (2).v-html可以识别html结构。 3.严重注意:v-html有安全性问题&#xff0…