C11 中是否有一种机制来确保否定整数常量的预期类型?

Is there a mechanism in C11 for ensuring expected types of negated integer constants?

我在 C 标准中找不到任何地方可以证明以下内容:

int n = -0x80000000 // set n to -2^31

假设 int 是 32 位的实现。明显的问题是整数常量的类型为 unsigned int,根据委员会标准草案 6.4.4.1 第 5 段中的 table。然后根据 6.5.3.3 第 3 段计算否定:

The result of the unary - operator is the negative of its (promoted) operand. The integer promotions are performed on the operand, and the result has the promoted type.

执行整数提升不会更改类型(unsigned int 保持为 unsigned int)。然后取负片。由于结果保留了提升后的类型,它以 2^32 为模进行缩减,得到 2^31(因此否定无效)。

将超出范围的值分配给 int 类型包含在以下内容中:

6.3.1.3 Signed and unsigned integers

1 When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.

2 Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type. 60)

3 Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

因此,最后,当我们尝试将有效的 int 值分配给 int 对象时,我们得到了实现定义的行为(假设 2 的补码没有陷阱表示)。

以下是标准保证具有预期结果:

int n = -(long long)0x80000000 // set n to -2^31

那么,您真的需要强制转换才能有效地进行范围内分配,还是我遗漏了什么?

如果 INT_MAX0x7fffffff,那么作为十六进制文字,0x80000000 的类型为 unsigned int 或更大,并且应用 - 是安全的.对于十进制文字,情况并非如此。如果 INT_MAX 大于 0x7fffffff 那么否定就已经是安全的 int.

编辑后的问题现在将结果分配给 int 类型的对象,从超出范围的值到 int 的转换是实现定义的。在实践中,它总是被定义为你想要的(模块化缩减),但标准并不能保证这一点。所有标准保证是实施必须记录转换是如何发生的。

恐怕你会说 将超出范围的值分配给 int 类型包含在以下内容中: 不适用于

unsigned int n = -0x80000000 // set n to -2^31

n 具有类型 unsigned int 并且值 2^31 没有超出 32 位 unsigned int.

的范围

EDIT:由于您更改了问题并将 n 设为 int,因此 3 申请32 位和更小的 ints 并且注释对于更大的 int 类型是不正确的:

3 Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

EDIT:第二个代码片段 int n = -(long long)0x80000000 // set n to -2^31 是正确的,因为值 -2147483648 确实适合 32 位 int.

请注意,将 n 初始化为此值的正确方法是(假设 32 位整数):

int n = -2147483647 - 1;  // set n to -2^31

此变体用于标准 headers <limits.h><stdint.h>。它不使用强制转换,所以你最后一个问题的答案是:不,你真的不需要强制转换以有效地为 -2^31 到 32 位 int.[=26 进行范围内分配=]

int n = -(long long)0x80000000

do you really need to cast up to validly make an in range assignment, or am I missing something?

怎么样:int n = -0x80000000LL;

没有演员表。

只需int n = INT_MIN

或者如果您必须 int n = -2147483648(与转换为 long long 相同)。

在 C 中混合使用十六进制文字和负数通常不是一个好主意,因为十六进制文字采用特定的符号格式。

we get implementation defined behavior when we try to assign a valid int value to an int object

如果您担心实现定义的行为,那么为什么要使用朴素的 int 类型而不是 int32_t

int 具有实现定义的大小和符号格式,这是问题的根源。 int32_t 保证是 32 位二进制补码。

C 中有符号和无符号类型的相对行为是一团糟,这是在 C89 委员会试图制定尽可能与预先存在的编译器行为一致的规则时出现的,而这些编译器通常与每个编译器都不一致其他.

除了手动确保它们被提升为保证足够大以容纳所有中间值的类型之外,没有任何可移植的方法来确保涉及整数常量的表达式将以预期的方式工作;如果你不需要超过 64 位的任何东西,"long long" 或 "unsigned long long" 就足够了;可以使用 UL 或 ULL 后缀将值强制为这些类型。