如果char被签名,是“char foo = 255”未定义的行为?
以下在使用Linux的x86机器上使用gcc 4.5.2进行编译时不会给出任何警告:
char foo = 255;
但是当我用-pedantic
,gcc说:
警告:在隐式常量转换中溢出
海湾合作委员会的行为方式是有点奇怪,这让我怀疑,如果我真的明白这个任务是怎么回事。 我认为,如果char
在POSIX上是8位长,并且是默认签名的,那么它不能保持255
。
在C标准中,它表示无符号整数溢出导致溢出,但是有符号整数溢出未定义。 那么这个任务是不确定的行为呢? 为什么海湾合作委员会这样做?
总结:结果是实现定义的,很可能是-1
,但至less在原理上是复杂的。
有关运算符和转换的溢出规则,以及有符号types和无符号types的规则有所不同 – 转换规则在C90和C99之间改变。
从C90开始,有符号整数运算符的操作符溢出(“overflow”意味着math结果不能用expression式的types表示)具有未定义的行为。 对于无符号整数操作数,行为被定义为通常的环绕(严格地说,标准并不把它称为“溢出”)。 但是你的声明:
char foo = 255;
不使用任何运算符( =
是初始化程序,而不是赋值),所以在这种情况下都不适用。
如果char
types可以表示值255
(这是真的,无论是无符号的char
或者如果CHAR_BIT >= 9
),那么当然行为是很好的定义。 int
expression式255
被隐式转换为char
。 (由于CHAR_BIT >= 8
,所以这种情况不能调用无符号的换行。)
否则,转换会产生无法存储在char
。
从C90开始,转换的结果是实现定义的 – 这意味着它将保证将foo
设置为char
types范围内的某个值,并且可以通过阅读实现的文档来确定该值是什么告诉你转换是如何工作的 (我从来没有看到一个实现,其中存储的值是-1
以外的任何东西,但原则上可能有任何结果。)
C99改变了定义,所以一个溢出的转换为一个有符号的types可以产生一个实现定义的结果或者引发一个实现定义的信号。
如果编译器select执行后者,则必须logging引发了哪个信号。
那么如果一个实现定义的信号被提出,会发生什么呢? 该标准第7.14节说:
完整的信号集合,它们的语义以及它们的缺省处理是实现定义的
对于我来说,对于信号的“默认处理”可能的行为范围是什么并不完全清楚。 在最坏的情况下,我想这样的信号可能会终止程序。 您可能会或可能无法定义捕获信号的信号处理程序。
7.14还说:
如果函数返回,如果sig的值是SIGFPE , SIGILL , SIGSEGV或其他与计算exception对应的实现定义的值,则行为是未定义的; 否则程序会在被中断的地方继续执行。
但我不认为这是适用的,因为溢出转换不是“计算exception”,因为这里使用的术语。 (除非实现定义的信号恰好是SIGFPE
, SIGILL
或SIGSEGV
– 但是这很愚蠢)。
所以最终,如果一个实现select提出一个响应溢出转换的信号,那么行为(不仅仅是结果)至less是实现定义的,并且可能存在未定义的情况。 无论如何,似乎没有任何便携的方式来处理这样的信号。
在实践中,我从来没有听说过利用C99中新的措辞的实现。 对于我所听说过的所有编译器来说,转换的结果都是实现定义的 – 很可能会产生你所期望的2的补码截断。 (而且我完全不相信C99的这个改变是个好主意,如果没有其他的话,这个答案大概需要3倍的时间。)
带符号的整型溢出只会在评估算术expression式的中间结果时出现未定义的行为,例如,在二进制冗余,一元递减等情况下。另外,如果值超出范围,则将浮点值转换为整数types会导致未定义的行为。
溢出整数转换为有符号types不会导致未定义的行为。 相反,它会生成实现定义的结果(可能会引发实现定义的信号)。
根据C11,6.3.1.3:
当整数types的值被转换为除
_Bool
以外的另一个整数types时,如果新types是有符号的,并且该值不能在其中表示; 结果是实现定义的或实现定义的信号被引发。