总结:结果是实现定义的,很可能是-1
,但它很复杂,至少在原则上是这样。
关于溢出的规则对于操作符和转换以及有符号类型和无符号类型是不同的——并且转换规则在 C90 和 C99 之间发生了变化。
从 C90 开始,带有符号整数操作数的运算符溢出(“溢出”意味着数学结果不能以表达式的类型表示)具有未定义的行为。对于无符号整数操作数,行为被很好地定义为通常的环绕(严格来说,标准并不称其为“溢出”)。但是你的声明:
char foo = 255;
不使用任何运算符( the=
是初始化程序,而不是赋值),因此在这种情况下都不适用。
如果 typechar
可以表示值255
(plain char
is unsigned 或 if都为真CHAR_BIT >= 9
),那么行为当然是明确定义的。int
表达式被255
隐式转换为char
。(因为CHAR_BIT >= 8
,这种特殊情况不可能调用无符号环绕。)
否则,转换会产生无法存储在char
.
从 C90 开始,转换的结果是实现定义的——这意味着它保证设置foo
为type 范围内的某个char
值,您可以通过阅读实现的文档来确定该值是什么,这需要告诉你是如何转换的。(我从未见过存储值不是 的实现-1
,但原则上任何结果都是可能的。)
C99 更改了定义,因此到有符号类型的溢出转换要么产生实现定义的结果,要么引发实现定义的信号。
如果编译器选择执行后者,那么它必须记录引发了哪个信号。
那么如果引发了实现定义的信号会发生什么?该标准的第 7.14 节说:
完整的信号集、它们的语义和它们的默认处理是实现定义的
(对我来说)信号的“默认处理”的可能行为范围并不完全清楚。在最坏的情况下,我想这样的信号可能会终止程序。您可能会也可能不会定义捕获信号的信号处理程序。
7.14 还说:
如果并且当函数返回时,如果 sig 的值是SIGFPE、
SIGILL、SIGSEGV或与计算异常对应的任何其他实现定义的值,则行为未定义;否则程序将在中断点恢复执行。
但我认为这并不适用,因为溢出转换不是此处使用的术语“计算异常”。(除非实现定义的信号恰好是SIGFPE
,SIGILL
或SIGSEGV
-- 但那会很愚蠢)。
因此,最终,如果实现选择引发信号以响应溢出转换,则行为(不仅仅是结果)至少是实现定义的,并且可能存在未定义的情况。无论如何,似乎没有任何可移植的方式来处理这样的信号。
在实践中,我从未听说过利用 C99 中新措辞的实现。对于我听说过的所有编译器,转换的结果是实现定义的——并且很可能会产生您对 2 补码截断的期望。(而且我完全不相信 C99 中的这种变化是一个好主意。如果不出意外,它使这个答案的时间大约是原本需要的时间的 3 倍。)