广告

C代码的意外

2021-06-25 Colin Walls 阅读:
C语言可以用多种不同的方式编写代码,但是完成完全一样的功能。但是有一个问题:有时候两段语句虽然表面上看起来实现完全一样的功能,实际却有细微的差别。

C语言非常灵活、更容易表达,这是C语言很成功并且一直没被“更好的”语言取代的一个原因。例如,其灵活性的一个表现就是,可以用多种不同的方式编写代码,但是完成完全一样的功能,因此每个人可以根据自己的喜好而形成不同的编码风格。但是有一个问题:有时候,两段代码虽然表面上看起来能完成同样的功能,但是却有细微的差别。这种情况可能在最简单的代码中发生,本文将探讨一些可能发生的情况。Ntjednc

在C语言中,利用几种不同的方式来完成同一件事,这是很常见的,这些方式达到的效果完全相同。例如,假设x是一个普通的int变量,以下每个语句都将完成同项的任务:Ntjednc

Ntjednc

使用其中的每一种方法,x都会加1。唯一可能的区别是,能力较弱的编译器可能会为后两种方法生成稍好一些的代码(这说明得到更好的编译器是值得的)。Ntjednc

上面两种形式的++运算符会产生相同的结果。但是,如果表达式的结果作为中间值被使用,那么前增量和后增量是不同的,因此:Ntjednc

y=x++; // y在增量前被赋值 Ntjednc

y=++x; // y在增量后被赋值 Ntjednc

有趣的是,后增量稍微“昂贵”一些,因为需要分配存储空间以保留x的旧值。但是,编译器可能会对此进行优化。如果在不使用表达式的值时分配存储,那么肯定需要一个新的编译器!Ntjednc

如果x不是int值自身,而是指向int值的指针,则加1会产生指针地址加4(在32位机器上)的效果。如果你对此感到惊讶,就有必要复习一下指针运算了。然而,有时看似等效的表达方式会有非常细微的差异……Ntjednc

对任何编程语言来说,你能做的最简单的事可能就是为变量赋值。所以,使用C语言时,我们可以这样写:Ntjednc

Ntjednc

当然,也可以更紧凑地写成这样:Ntjednc

Ntjednc

它们是100%等效的,对吗?大多数情况下,这两种表达方式是完全等效的,但是(至少)在四种情况下,选择一种或另一种可能会有不同:Ntjednc

首先,也是最平淡无奇的,每个变量都是独立的,这时或许加个注释,说明为什么将其设置为该值比较合适。Ntjednc

其次,编写可维护的代码肯定比较好。也许在将来的某个时候,可能需要更改代码,使这三个变量都设置为不同的值。第一种格式更容易修改。Ntjednc

第三个原因是当编译器不合标准时,可能会为第一种形式的语句生成这样的代码:Ntjednc

Ntjednc

第二种语句会提示r0只需要加载一次。同样,更好的编译器不需要提示。Ntjednc

最后是执行顺序的问题。在第一种语句中,很明显将先赋值alpha,最后赋值gamma。编译器会这样解释第二种语句:Ntjednc

Ntjednc

这意味着赋值顺序颠倒了。但有关系吗?大多数时候,没有。但如果这些是设备寄存器,而不是普通变量,就可能会有很大的不同。硬件需要设置值然后按精确的顺序加载,这是很常见的。Ntjednc

所以我想说,应该避免在一个语句中多次赋值。Ntjednc

总之,虽然C是一种轻量型编程语言,但如果简化完成任务的方式,C语言还可以更变得轻,从而可能产生更清晰、更易于维护的代码。Ntjednc

网友谈经验:Ntjednc

@ GeorgeD  Ntjednc

产生意外的另一个主要原因是中断。例如,在赋值顺序的例子中:Ntjednc

alpha = beta = gamma = 99;Ntjednc

有可能在beta被赋值99之后,一个中断立马重新将其赋值为77。Ntjednc

那么结果将是:Ntjednc

gamma = 99;Ntjednc
beta = gamma;Ntjednc
beta = 77;Ntjednc
alpha = 77Ntjednc

但是,在单独赋值时:Ntjednc

alpha = 99;Ntjednc
beta = 99;Ntjednc
gamma = 99;Ntjednc

只有beta被中断重新赋值为 77。 Ntjednc

人们会期望这个复合语句中具有一些原子性,但这在C语言中没有定义。程序员可以在复合语句的执行期间禁用中断,如果他愿意的话。Ntjednc

@ kalpakNtjednc

我会说这些意外是源于“聪明过分”的代码。Ntjednc

现实中一个程序员需要写多少次“alpha = beta = gamma = 99;”?Ntjednc

换句话说,与所带来的风险相比,此类优化代码的优化产生的好处有多大?Ntjednc

此外,这样的赋值使注释变得困难,因此可读性很差。Ntjednc

当程序员编写这样的代码时,我会质疑架构师和设计师的能力。Ntjednc

在过去的日子里,内存不足且时钟还是KHz,有时不得不为了解析效率而牺牲可读性。Ntjednc

而现在有非常好的优化编译器可用,内存充足,处理器速度达到 100 MHz,我们必须说这些代码是不安全的或错误的。Ntjednc

作者介绍:Ntjednc

Colin Walls是西门子公司Mentor的嵌入式软件技术专家,拥有超过40年电子行业的经验,主要专注于嵌入式软件。他经常在大会和研讨会上发表演讲,并撰写了大量技术文章和两本关于嵌入式软件的书籍。Ntjednc

Ntjednc

(原文刊登于Aspencore旗下embedded网站,参考链接:Surprises in C code,由Jenny Liao编译。)Ntjednc

本文为EDN电子技术设计 原创文章,禁止转载。请尊重知识产权,违者本司保留追究责任的权利。
  • 微信扫一扫
    一键转发
  • 最前沿的电子设计资讯
    请关注“电子技术设计微信公众号”
  • 酒店房间装有摄像头?防偷窥神器是怎样检测出的? EDN小编在某科技类微信群看到了一条关于“酒店马桶内装有摄像头,用防偷拍神器可以检测到”的短视频,群里的科技大拿们对此展开了热烈讨论。有人提问说,这到底是摄像头还是智能马桶的红外感应器?有人说,看来智慧家居给偷拍产业提供了隐藏。还有人认为,这很可能是女主播为了带货拍的广告视频……那么事情的真相是什么?
  • 小型太阳能光伏电源的串联与并联线性稳压对比 对小型太阳能光伏阵列而言,使用线性稳压方案会比较简单。本设计实例将针对此类系统解读,重点关注串联稳压器拓扑与并联稳压器拓扑的相对优势。
  • 如何为SiC MOSFET选择合适的栅极驱动器 尽管碳化硅(SiC)具有开关速度更快和效率更高等一系列优势,但它也带来了一些设计挑战,我们可以通过选择合适的栅极驱动器来予以解决。
  • 自耦变压器和风扇 由于我的SPICE版本中并不包括自耦变压器,因此必须设计一个使用两个1:1匝数比变压器的模型...
  • 从技术角度分析,GaN和SiC功率器件上量还欠什么? 氮化镓(GaN)和碳化硅(SiC)这两种新器件正在推动电力电子行业发生重大变化,它们在汽车、数据中心、可再生能源、航空航天和电机驱动等多个行业取得了长足的进步。在由AspenCore集团举办的PowerUP Expo大会上,演讲嘉宾们深入探讨了包括GaN和SiC在内的宽禁带(WBG)器件的技术优势以及发展趋势。
  • 自耦变压器SPICE建模 自耦变压器又称为单绕组变压器,可分升压变压器及降压变压器;它是一种只有一组线圈的变压器,其中一个线圈作为另一线圈的一部份...
  • 555 定时器 IC 50 岁了,为何它能经久不衰? 自 1972 年推出以来,555 定时器 IC一直在市场上广泛使用。在 IC 技术编年史中,那是恐龙时代。这种基本未改变的 IC 已经生产了很长时间,目前仍有十几家厂商提供这种芯片。我找不到具体的数字,但我怀疑每年仍有数百万人在使用传统和新设计。那么也许是时候让 555 退役并在那些传统的晶圆厂队列中为其他更新的模拟 IC 腾出空间了?
  • 光罩制作前的芯片工程设计变更 随着芯片集成度越来越高和功能越来越复杂,在芯片开发过程中很容易产生缺陷。为确保芯片中的功能不受到影响,在流片前修复这些缺陷非常重要。本文将介绍如何通过手工修改网表代码或使用Conformal或Formality等工具执行工程设计变更单 (ECO),来修复RTL固定后发现的缺陷。
  • 对比美光、三星、SK 海力士的DDR5内存 本文比较了美光、三星和 SK 海力士的 DDR4-3200 和 DDR5-4800 芯片的 DDR5 芯片尺寸、存储密度、DRAM 单元尺寸和设计规则。
  • 六边形截面的EMI隔离罩设计 采用截面为六边形设计的蜂巢状隔离罩有助于形成很大的通风面积,同时又能够有效的防止EMI/RFI…
  • 自动驾驶汽车有多复杂? 自动驾驶汽车有许多棘手的技术问题仍远未解决。在我看来,这里有三个关键问题:为什么自动驾驶汽车问题如此难以解决?不同的自动驾驶汽车用例如何影响自动驾驶汽车问题?自动驾驶汽车用例的部署将如何发展?
  • 自动驾驶汽车神话:十二大误区 尽管存在巨大风险,但自动驾驶汽车(AV)行业仍在为当前的测试和未来的广泛部署争取有利的法规待遇。这场争取限制有意义的监管的竞赛,却依赖于一些极易被揭穿的关于自动驾驶汽车的神话。为了创造公平的竞争环境,本文列出了业内人士所常常提到的自动驾驶汽车“十二大”误区,它们通常是AV监管中的疏漏之处。
广告
热门推荐
广告
广告
EE直播间
在线研讨会
广告
广告
面包芯语
广告
向右滑动:上一篇 向左滑动:下一篇 我知道了