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 *T2

T1 a,b;
T2 a,b;

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

struct foo * a, b;

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch:为现代搜索工作流程和生成式人工智能应用程序铺平道路

作者:Matt Riley Elastic 的创新投资支持开放的生态系统和更简单的开发者体验。 在本博客中,我们希望分享 Elastic 为简化你构建 AI 应用程序的体验而进行的投资。 我们知道,开发人员必须在当今快速发展的人工智能环境中保持灵活性。 然而&a…

xilinx系列FPGA基于VIVADO的pin delay列表生成说明

目录 1 概述2 示例平台3 操作说明4 注意事项 xilinx系列FPGA基于VIVADO的pin delay列表生成说明 1 概述 本文用于讲诉xilinx系列FPGA基于VIVADO的pin delay列表生成说明,以及一些注意事项,为FPGA设计人员探明道路。 Pin delay 即FPGA内部die到pin的延时…

爱芯元智AX650N部署yolov8s 自定义模型

爱芯元智AX650N部署yolov8s 自定义模型 本博客将向你展示零基础一步步的部署好自己的yolov8s模型(博主展示的是自己训练的手写数字识别模型),本博客教你从训练模型到转化成利于Pulsar2 工具量化部署到开发板上 训练自己的YOLOv8s模型 准备自…

机器视觉新功能上线:同步训练多个模型,智造的脚步又加快了!

“AI视觉”的应用,为当下诸多企业的生产智能化打开了新的想象空间。其中,深度学习作为AI视觉的核心技术,在实际应用中往往需要经历一个耗时较长的阶段——深度学习神经网络模型训练。其目的是通过使用已标注的数据集来训练模型,使…

Inference with C# BERT NLP Deep Learning and ONNX Runtime

目录 效果 测试一 测试二 测试三 模型信息 项目 代码 下载 Inference with C# BERT NLP Deep Learning and ONNX Runtime 效果 测试一 Context :Bob is walking through the woods collecting blueberries and strawberries to make a pie. Question …

传统算法:使用 Pygame 实现插入排序

使用 Pygame 模块实现了插入排序的动画演示。首先,它生成一个包含随机整数的数组,并通过 Pygame 在屏幕上绘制这个数组的条形图。接着,通过插入排序算法对数组进行排序,动画效果可视化每一步的排序过程。在排序的过程中,程序将当前元素插入到已排序的部分,通过适度的延迟…

每日一练2023.12.1——输出GPLT【PTA】

题目链接:L1-023 输出GPLT 题目要求: 给定一个长度不超过10000的、仅由英文字母构成的字符串。请将字符重新调整顺序,按GPLTGPLT....这样的顺序输出,并忽略其它字符。当然,四种字符(不区分大小写&#x…

《opencv实用探索·七》一文看懂图像卷积运算

1、图像卷积使用场景 图像卷积是图像处理中的一种常用的算法,它是一种基本的滤波技术,通过卷积核(也称为滤波器)对图像进行操作,使用场景如下: 模糊(Blur): 使用加权平…

C++入门篇(零) C++入门篇概述

目录 一、C概述 1. 什么是C 2. C的发展史 3. C的工作领域 4. C关键字(C98) 二、C入门篇导论 一、C概述 1. 什么是C C是基于C语言而产生的计算机程序设计语言,支持多重编程模式,包括过程化程序设计、数据抽象、面向对象程序设计、泛型程序设计和设计模式…

Maven无法拉取依赖/构建失败操作步骤(基本都能解决)

首先检查配置文件,确认配置文件没有问题(也可以直接用同事的配置文件(记得修改文件里的本地仓库地址)) 1.file->Invalidate Caches清除缓存重启(简单粗暴,但最有效) 2.刷新maven以及mvn clean,多刷几次,看看还有没有报红的依赖…

Python 中 AttributeError: Int object Has No Attribute 错误

int 数据类型是最基本和最原始的数据类型之一,它不仅在 Python 中,而且在其他几种编程语言中都用于存储和表示整数。 只要没有小数点,int 数据类型就可以存储任何正整数或负整数。 本篇文章重点介绍并提供了一种解决方案,以应对我…

基于Netty的网络调用实现

作为一个分布式消息队列,通信的质量至关重要。基于TCP协议和Socket实现一个高效、稳定的通信程序并不容易,有很多大大小小的“坑”等待着经验不足的开发者。RocketMQ选择不重复发明轮子,基于Netty库来实现底层的通信功能。 1 Netty介绍 Net…

TCP报文解析

1.端口号 标记同一台计算机上的不同进程 源端口:占2个字节,源端口和IP的作用是标记报文的返回地址。 目的端口:占2个字节,指明接收方计算机上的应用程序接口。 TCP报头中的源端口号和目的端口号同IP报头中的源IP和目的IP唯一确定一…

马蹄集第34周

1.战神的对称谜题 不知道为什么超时&#xff01; def main():s input()result 0for i in range(len(s)):l i - 1r i 1while l > 0 and r < len(s) and s[l] s[r]:result max(result, r - l 1)l - 1r 1l ir i 1while l > 0 and r < len(s) and s[l] s…

二分查找与搜索树高频问题

关卡名 逢试必考的二分查找 我会了✔️ 内容 1.山脉数组的峰顶索引 ✔️ 2.旋转数字的最小数字 ✔️ 3.寻找缺失数字 ✔️ 4.优化求平方根 ✔️ 5.中序与搜索树原理 ✔️ 6.二叉搜索树中搜索特定值 ✔️ 7.验证二叉搜索树 ✔️ 基于二分查找思想&#xff0c;可以拓展出很…

【PUSDN】WebStorm中报错Switch language version to React JSX

简述 WebStorm中报错Switch language version to React JSX 可能本页面的写法是其他语法。所以可以不用管。 测试项目&#xff1a;ant design vue pro 前情提示 系统&#xff1a; 一说 同步更新最新版、完整版请移步PUSDN Powered By PUSDN - 平行宇宙软件开发者网www.pusdn…

算法学习—排序

排序算法 一、选择排序 1.算法简介 选择排序是一个简单直观的排序方法&#xff0c;它的工作原理很简单&#xff0c;首先从未排序序列中找到最大的元素&#xff0c;放到已排序序列的末尾&#xff0c;重复上述步骤&#xff0c;直到所有元素排序完毕。 2.算法描述 1&#xff…

C语言-预处理与库

预处理、动态库、静态库 1. 声明与定义分离 一个源文件对应一个头文件 注意&#xff1a; 头文件名以 .h 作为后缀头文件名要与对应的原文件名 一致 例&#xff1a; 源文件&#xff1a;01_code.c #include <stdio.h> int num01 10; int num02 20; void add(int a, in…

uniapp 使用web-view外接三方

来源 前阵子有个需求是需要在原有的项目上加入一个电子签名的功能&#xff0c;为了兼容性和复用性后面解决方法是将这个电子签名写在一个新的项目中&#xff0c;然后原有的项目使用web-view接入这个电子签名项目&#xff1b; 最近又有一个需求&#xff0c;是需要接入第三方的…

蓝桥杯每日一题2023.11.30

题目描述 九数组分数 - 蓝桥云课 (lanqiao.cn) 题目分析 此题目实际上是使用dfs进行数字确定&#xff0c;每次循环中将当前数字与剩下的数字进行交换 eg.1与2、3、4、、、进行交换 2与3、4、、、进行交换 填空位置将其恢复原来位置即可&#xff0c;也就直接将其交换回去即可…