众所周知,有符号整数溢出是一种未定义的行为。但是在C 11cstdint
文档中有一些有趣的东西:
带符号整数类型,宽度分别为8、16、32和64位,不带填充位,并使用2的补码表示负值(仅在实现直接支持该类型时提供)
参见链接
我的问题是:既然标准明确规定,int8_t
、int16_t
、int32_t
和int64_t
负数是2的补码,那么这些类型的溢出仍然是一种未定义的行为吗?
编辑我检查了C 11和C11标准,以下是我的发现:
C 11,§18.4.1:
标题定义了所有函数、类型和宏,与C标准中的7.20相同。
C11,§7.20.1.1:
typedef nameintN_t
指定一个有符号整数类型,宽度为N,无填充位,并用2的补码表示。因此,int8_t
表示宽度正好为8位的有符号整数类型。
我敢打赌。
根据标准文件(第4页和第5页):
1.3.24未定义的行为
本国际标准不要求的行为
[注:当本国际标准省略任何明确的行为定义,或当程序使用错误的构造或错误的数据时,可能会出现未定义的行为。允许的未定义行为范围从完全忽略情况到结果不可预测,到在翻译或程序执行过程中以环境特有的文件化方式表现(无论是否发出诊断信息),终止翻译或执行(发出诊断信息)。许多错误的程序结构不会产生未定义的行为;他们需要被诊断[尾注]
仅仅因为一个类型被定义为使用2s补码表示,并不意味着该类型中的算术溢出就被定义了。
有符号算术溢出的未定义行为用于启用优化;例如,编译器可以假设如果a
这些类型的溢出仍然是一种未定义的行为吗?
是的。根据C 11标准第5/4段(关于一般的任何表述):
如果在表达式求值期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为未定义。[...]
2的补码表示用于这些有符号类型的事实并不意味着在计算这些类型的表达式时使用算术模2^n。
另一方面,关于无符号算术,标准明确规定(第3.9.1/4段):
无符号整数,声明为无符号
,应遵守算术模2^n定律,其中n是该特定大小整数的值表示中的位数
这意味着无符号算术运算的结果总是“数学定义的”,并且结果总是在可表示的范围内;因此,5/4不适用。脚注46对此进行了解释:
46)这意味着无符号算术不会溢出,因为结果无符号整数类型不能表示的结果被模数化,该数字比结果无符号整数类型可以表示的最大值大一倍。
最近,有符号整数溢出在C和C中没有正式定义,这引起了很多关注。然而,给定的实现可能会选择定义它;在C语言中,实现可以设置
未定义行为的一个例子是在flow上的整数行为 有没有一个历史的或者(甚至更好!)造成这种差异的技术原因是什么?
C99标准中哪里说有符号整数溢出是未定义的行为? 我看到关于无符号整数溢出的评论定义得很好(看看为什么定义了无符号整数溢出行为,但没有定义有符号整数溢出?)在第6.2.5节中: 涉及无符号操作数的计算永远不会溢出,因为不能由结果无符号整数类型表示的结果将被减少为比结果类型可以表示的最大值大一的数的模。 但是我在附录J中查看了未定义的行为,我只在列表中看到了这些类似的项目: 具有有符号提升类型的表达
我正在读一篇关于整数安全性的文章。以下是链接:http://ptgmedia.pearsoncmg.com/images/0321335724/samplechapter/seacord_ch05.pdf 在第166页,有这样一句话: 涉及无符号操作数的计算永远不会过流,因为不能由结果无符号整数类型表示的结果将被模化为比结果类型可以表示的最大值大一的数字。 这是什么意思?感谢您的回复。
通过这个答案,我知道: 由于计算导致的有符号溢出在C 20中仍然是未定义的行为,而由于转换引起的有符号溢出在C 20中定义得很好(这是为Pre-C 20定义的实现)。 这种由于转换引起的有符号溢出的变化是因为来自C 20编译器的有符号溢出需要使用二进制补码。 我的问题是: 如果编译器需要使用C 20中的2的补码,那么为什么由于计算定义良好而导致的有符号溢出就像由于转换而导致的有符号溢出一样呢? 也
Rust在调试和发布模式下处理有符号整数溢出的方式不同。当它发生时,Rust在调试模式下会恐慌,而在发布模式下会默默地执行两个补码的包装。 据我所知,C/C将有符号整数溢出视为未定义的行为,部分原因是: 在C标准化的时候,表示有符号整数的不同底层架构,例如补码,可能仍在某个地方使用。编译器不能假设硬件中如何处理溢出。 后来的编译器因此做出假设,例如两个正整数的总和也必须是正的,以生成优化的机器代码