依赖整数提升是一种糟糕的编程习惯吗?

Is relying on integer promotion a bad programming practice?

我目前正在为嵌入式系统(C 和 C++)编写一些代码,在尝试尽量减少内存使用时,我注意到我使用了很多依赖于整数提升的代码。例如(据我所知这段代码在 c 和 c++ 中是相同的):

uint8_t brightness = 40;
uint8_t maxval = 255;
uint8_t localoutput = (brightness * maxval) / 100;

因此,即使亮度 * 255 大于可以存储在 uint8_t 中的亮度,这仍然会产生正确的结果,因为如果我是正确的,整数提升。 亮度是一个百分比,因此它不应高于 100,因此本地输出不应高于 255。我的问题是是否有任何意外行为(例如亮度 * maxval 大于 255,因此有溢出)或任何显着差异这种语法在 c++ 和 c 之间是如何处理的。貌似只是输出正确答案,还是更推荐变量类型为uint16_t,因为中间计算可能会高于255,把记忆力减退当成理所当然。

您的问题提出了 C 编程和一般编程中的一个重要问题:程序是否在所有情况下都按预期运行?

表达式 (brightness * maxval) / 100 计算的中间值 brightness * maxval 可能超出用于计算它的类型的范围。在 Python 和其他一些语言中,这不是问题,因为整数没有限制范围,但在 C、C++、java、javascript 和许多其他语言中,整数类型具有固定位数,因此乘法可以超出此范围。

程序员有责任确定操作数的范围确保乘法不会溢出。这需要很好地理解整数提升和转换规则,这些规则因一种语言而异,并且在 C 中有些棘手,尤其是对于混合有符号和无符号类型的操作数。

在您的特定情况下,brightnessmaxval 都具有小于 int 的类型,因此它们被提升为具有相同值的 int 并且乘法产生int 值。如果 brightness0100 范围内的百分比,则结果在 025500 范围内,C 标准保证在该范围内类型 int 的范围,并将此数字除以 100 产生范围 0100 的值,范围 int,并且在目标类型 uint8_t 的范围内,因此操作已完全定义。

这个过程是应该记录在评论中还是应该用调试断言来验证是本地编码规则的问题。将操作数的顺序更改为 maxval * brightness / 100 并可能使用更明确的值和变量名称可能有助于 reader:

uint8_t brightness100 = 40;
uint8_t localoutput = 255 * brightness100 / 100;

这个问题比 整数提升 的问题更普遍,所有此类计算都应该针对极端情况和值范围进行分析。自动化工具可以帮助执行范围分析和优化编译器来改进代码生成,但这是一个难题。