6

基本上我要求翻译:

GCC 不使用 C99 中给出的纬度仅将带符号的 `<<' 的某些方面视为未定义,但这可能会发生变化。

GCC 4.8.1 手册,第 4.5 段

给出了什么“纬度”?哪些特定方面?

4

4 回答 4

3

The informations on the left shift operator in C99 draft standard (ISO/IEC9899:TC3, aka WG14/N1256) are rather scant.

Section 6.5.7 (bitwise shift operators) has already been cited by Alter Mann.

Annex J, section J.2 (Undefined behavior) says the following:

The behavior is undefined in the following circumstances:
[...]

— An expression is shifted by a negative number or by an amount greater than or equal to the width of the promoted expression (6.5.7).

— An expression having signed promoted type is left-shifted and either the value of the expression is negative or the result of shifting would be not be representable in the promoted type (6.5.7).

I don't think it is allowed for a conforming implementation to make some mandatory undefined behavior defined. If I'm not mistaken an implementation is allowed to define unspecified behavior (but it is not required to) and is required to specify implementation defined behavior, but undefined behavior cannot be specified. This doesn't mean that an implementation cannot choose a sane meaningful behavior, but it cannot let the user rely on that (it cannot "specify it").

I admit I'm not completely sure of that. Hope this helps.

Edit: Upon further reflection I think a conforming implementation can specify a behavior for what the standard deems undefined behavior, but the resulting program cannot be called conforming (see section 3.4.3).

于 2013-08-24T04:24:59.787 回答
3

C99 §6.5.7/3-4 列出了关于左移运算符 ( <<) 的两个特定的未定义行为:

3) [...] 如果右操作数的值为负数或大于或等于提升的左操作数的宽度,则行为未定义。

4) 结果E1 << E2是[...]。如果E1有一个有符号类型和非负值,并且E1× 2E2在结果类型中是可表示的,那么这就是结果值;否则,行为未定义。

我不能说 GCC 在这些情况下究竟表现如何。在这些情况下给出明确的行为是非常受欢迎的;但是,当使用其他编译器编译时,此类代码仍将是未定义的。

我的猜测是,GCC 将有符号左移与无符号左移相同地处理——也就是说,如果计算x << y(signed)((unsigned)x << y)通过位移(可能丢弃任何高位),然后将结果重新解释为有符号数量。这意味着有符号整数的最高有效值位被移入符号位;从算术的角度来看,这似乎有点奇怪,但从按位的角度来看,这是完全合理的。

于 2013-08-24T04:11:45.813 回答
3

其他答案已经指出了哪些方面<<是未定义的行为。我的猜测是你想要将 gcc 翻译成通用语言。

如果 C 标准未定义行为,则编译器实现者可以在发生这种情况时采取“自由度”来做任何适合他们的事情。特别是,他们不必对这种情况进行诊断或检测,并且可以假装它从未发生过。程序员有责任编写他的程序,以便始终定义其行为。

在左移的情况下,这意味着编译器不必检查溢出并且可以假装像这样的循环

for (int i = 1; i > 0; i <<= a) {
 .... change a in some complicated way ...
}

永远不会终止。

您引用的句子表明他们还没有做这样的事情,但是 gcc 的未来版本可能会这样做。

于 2013-08-24T08:47:10.567 回答
0

当前的文档说:

  • 对有符号整数(C90 6.3、C99 和 C11 6.5)的一些按位运算的结果。

    [...]

    作为对 C 语言的扩展,GCC 不使用 C99 和 C11 中给出的纬度仅将带符号的某些方面<<视为未定义。但是,-fsanitize=shift(and -fsanitize=undefined) 将诊断此类情况。在需要常量表达式的地方也会对它们进行诊断。

即它不会再改变了。相当模糊的措辞意味着在 C99、C11 中明确提到的未定义行为,即

[...] 如果 E1 具有有符号类型和非负值,并且 E1 x 2 E2可以在结果类型中表示,那么这就是结果值;否则,行为未定义。

因为那是从 C89 到 C99的变化,即 C89 中不存在,而只有C99 和 C11。所以 GCC 说 a) 整数将具有 2 的补码表示,并且 b) 移位运算符如果左移小于左操作数的宽度,结果是 2 的补码表示左移,即好像数字是第一次转换到相同宽度的无符号,然后对该无符号值执行位移,最后转换为具有相同表示的 2 的补码有符号数。

然而,在 C89 中也明确未定义提升的左操作数的宽度或更多,因此它不属于“C99、C11 中提到的明确未定义行为”。

于 2020-10-11T16:35:24.213 回答