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

news2024/11/14 14:09:38

在严格意义上的编译过程开始之前,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 *T2

T1 a,b;
T2 a,b;

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

struct foo * a, b;

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

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

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

相关文章

【IEEE出版|往届均已成功EI检索】2024年第四届消费电子与计算机工程国际学术会议(ICCECE 2024)

2024年第四届消费电子与计算机工程国际学术会议(ICCECE 2024) 2024 4th International Conference on Consumer Electronics and Computer Engineering 进入21世纪以来,计算机技术的高速发展带来了消费电子产品的快速更迭。在技术迅速发展历…

NSSCTF第14页(2)

[UUCTF 2022 新生赛]ezpop 提示说看看反序列化字符串逃逸 PHP反序列化字符串逃逸_php反序列化逃逸-CSDN博客 php反序列化字符逃逸_php反序列化逃逸_Leekos的博客-CSDN博客 buuctf刷题9 (反序列化逃逸&shtml-SSI远程命令执行&idna与utf-8编码漏洞)_extract($_post);…

深度学习:什么是知识蒸馏(Knowledge Distillation)

1 概况 1.1 定义 知识蒸馏(Knowledge Distillation)是一种深度学习技术,旨在将一个复杂模型(通常称为“教师模型”)的知识转移到一个更简单、更小的模型(称为“学生模型”)中。这一技术由Hint…

CityEngine2023安装与快速入门

目录 0 引言1 安装2 CityEngine官方示例2.1 官方地址2.2 导入示例工程 3 结尾 🙋‍♂️ 作者:海码007📜 专栏:CityEngine专栏💥 标题:CityEngine2023安装与快速入门❣️ 寄语:书到用时方恨少&am…

sublime Text使用

1、增加install 命令面板 工具(tool)->控制面板(command palette) -> 输入install ->安装第一个install package controller,以下安装过了,所以没展示 2、安装json格式化工具 点击install package,等几秒会进入控制面板&#xff0…

机器学习入门(第六天)——支持向量机(升维打击)

Support vector machines 知识树 Knowledge tree 苹果表示重点 间隔:使用了几何间隔,保证w b的度量,感知机则是函数间隔 间隔最大化思想:则是支持向量机的独有,这使得它找到最优超平面 核函数:面试当中可…

什么是软阈值,硬阈值,软聚类,硬聚类!!软和硬指的是什么呢?详细解释看这里!!!

文章目录 一、软阈值和硬阈值的基本概念和区别二、软聚类和硬聚类的详细概念和区别 一、软阈值和硬阈值的基本概念和区别 在我所研究的领域中,经常出现小波降噪,就拿小波降噪举例子吧!! 在信号处理中,小波降噪是一种…

解析编程中的技术迷题:常见挑战与应对策略

前言 在数字化时代的浪潮中,编程已经成为了一项至关重要的技能。无论是在软件开发、数据分析、人工智能还是互联网领域,编程都扮演着不可或缺的角色。作为一种创造性的活动,编程不仅仅是代码的书写,更是一种解决问题和创新的思维方…

数据结构(六):堆介绍及面试常考算法

一、堆介绍 1、定义 堆是一种图的树形结构,被用于实现“优先队列”(priority queues)。优先队列是一种数据结构,可以自由添加数据,但取出数据时要从最小值开始按顺序取出。在堆的树形结构中,各个顶点被称…

web:ics-05(本地文件包含漏洞、preg_replace函数/e漏洞、php伪协议读取文件)

题目 打开页面显示如下 只有这个页面能打开 显示如下 用dirsearch扫一下 查看了一下,发现没什么用 查看页面源代码 返回了,写入的参数,猜测可能有文件包含漏洞 用php伪协议读取文件 构造payload ?pagephp://filter/readconvert.base64-en…

java源码-流程控制

1、Java流程控制 主要涉及三大流程控制:顺序、分支、循环 如下图: 1)流程2 存在对用户名和密码的校验,是否为空,存在分支控制 2)流程3 用户名和密码在数据库是否存在,存在分支控制 3&#xff0…

深入了解Java8新特性-日期时间API之TemporalAdjusters与TemporalAdjuster

阅读建议 嗨,伙计!刷到这篇文章咱们就是有缘人,在阅读这篇文章前我有一些建议: 本篇文章大概10000多字,预计阅读时间长需要10分钟。本篇文章的实战性、理论性较强,是一篇质量分数较高的技术干货文章&…

Linux基础操作二:Linux系统介绍

1、系统启动过程 Linux系统的启动过程并不是大家想象中的那么复杂,其过程可以分为5个阶段: 内核的引导。运行 init。系统初始化。建立终端 。用户登录系统。 1.1、内核引导 当计算机打开电源后,首先是BIOS开机自检,按照BIOS中…

六、shell编程

详见 《shell编程超详细入门教程》

enum枚举类 - Java

枚举类 一、引入二、介绍三、实现方式1、 自定义类实现枚举小结 2、 enum关键字实现枚举 四、使用enum注意事项五、练习六、enum常用方法七、练习八、enum和接口 一、引入 要求创建季节(Season)对象,请设计并完成。 传统方法: public class Enumerati…

【秒懂JDK,JRE,JVM的关系】

🌠作者:TheMythWS. 🎇座右铭:不走心的努力都是在敷衍自己,让自己所做的选择,熠熠发光。 ​ JDK与JRE与JVM的关系 先用一张图来直观感受JDK JRE JVM之间的关系: JDK与JRE的关系 先说JDK和JRE…

绩效考核管理项目|记录2

给界面添加筛选条件并且把搜索功能实现 这段代码写入搜索方法里面就能实现功能。 private void bingdgv(){//筛选项:用户名、职位代码、是否辞职string userName txtUserName.Text.Trim();int baseTypeId (int)base_cbx.SelectedValue;bool isStop isdel_ckb.Che…

Loki安装部署

Loki安装部署 1、Loki介绍 Loki 是受 Prometheus 启发由 Grafana Labs 团队开源的水平可扩展,高度可用的多租户日志聚合系统。开发语 言: Google Go。它的设计具有很高的成本效益,并且易于操作。使用标签来作为索引,而不是对全文进行检索&…

人工智能“排头兵”,探访福州多地 AI 智算实践

生成式 AI 在 2023 年再次引爆 IT 技术发展,福建作为数字中国的重要策源地,也是国家数字经济创新发展试验区,在人工智能方面拥有良好的产业基础和人才优势,同时近期出台的《福建省促进人工智能产业发展十条措施》,为福…

基于web的舞蹈选课管理系统设计与实现

基于web的舞蹈选课管理系统的设计与实现 摘 要 现如今,学生网上选课越来越便利,但是大多数在线下教育机构工作的教务人员,在进行教务管理的时候,并没有完全采用信息化管理。随着选择线下教育机构的人数不断增加,人工…