大于 32767 的短整型值

Posted

技术标签:

【中文标题】大于 32767 的短整型值【英文标题】:Short int values above 32767 【发布时间】:2013-06-30 03:17:18 【问题描述】:

当试图在 C 中存储高于 32,767 的短整数值时,为了看看会发生什么,我注意到打印到屏幕上的结果是我试图存储的数字 - 65,536。例如。如果我尝试将 65,536 存储在一个短变量中,则打印到屏幕上的数字为 0。如果我尝试存储 32,768,我将 -32,768 打印到屏幕上。如果我尝试存储 65,546 并将其打印到屏幕上,我得到 10。我想你明白了。为什么我会看到这些结果?

【问题讨论】:

short 是 2 个字节(通常,视情况而定)。 2 字节 = 16 位。第一个是符号,因此您最多可以存储 2^15 的值,然后才会翻转并变为负数。 en.wikipedia.org/wiki/2%27s_complement (它实际上是有符号类型的未定义行为,尽管我从来不知道除了取模值之外还有什么实现) 【参考方案1】:

整数值使用Twos Complement 存储。在二进制补码中,可能值的范围是 -2^n2^n-1,其中 n 是用于存储的位数。由于存储的方式,当你超过2^n-1时,你最终会回到2^n

short 使用 16 位(15 位用于数字存储,最后一位是符号)。

编辑:请记住,不保证会发生此行为。编程语言的行为可能完全不同。从技术上讲,高于或低于最大/最小值是未定义的行为,应该这样对待。 (感谢 Eric Postpischil 让我保持警觉)

【讨论】:

行为未定义。你不能依赖二进制补码。即使底层硬件使用二进制补码,编译器中的优化也会导致包含溢出的代码被忽略或以令人惊讶的方式更改。 这是正确的,但他看到他所看到的原因是底层的二元补码没有被抽象或优化掉。绝对不是可以依赖的行为,而是可以解释的行为。 那么答案应该明确说明这一点。请记住,这个答案是持久的,许多其他寻求理解类似问题的人可能会看到这个答案。不加限制地声明整数值使用二进制补码存储是错误的,即使在底层硬件中它是正确的,它也是误导性的,因为语言规则不需要 C 语言中的溢出来产生与二进制补码中的溢出相同的行为。 【参考方案2】:

当一个值转换为有符号整数类型但不能以该类型表示时,会发生溢出,并且行为未定义。通常会看到结果,好像使用了二进制补码编码并且好像存储了低位(或者,等效地,值以适当的 2 次方为模包装)。但是,您不能依赖此行为。 C 标准说,当有符号整数溢出发生时,行为是未定义的。所以编译器可能会以令人惊讶的方式行事。

考虑这段代码,它是为 short int 为 16 位的目标编译的:

void foo(int a, int b)

    if (a)
    
        short int x = b;
        printf("x is %hd.\n", x);
    
    else
    
        printf("x is not assigned.\n");
    


void bar(int a, int b)

    if (b == 65536)
        foo(a, b);

如果bshort int 的范围内,请注意foo 本身就是一个完美的函数。而bar 是一个非常好的函数,只要它只在a 等于0 或b 不等于65536 时调用。

虽然编译器在bar 中内联foo,但它可能从b 此时必须为65536 的事实推断出short int x = b; 中会出现溢出。这意味着要么永远不会采用此路径(即a 必须为零),要么允许任何行为(因为溢出时的行为未由 C 标准定义)。在任何一种情况下,编译器都可以随意省略此代码路径。因此,为了优化,编译器可以省略此路径并仅为printf("x is not assigned.\n"); 生成代码。如果您随后执行包含bar(1, 65536) 的代码,则输出将是“x 未分配”!

编译器确实会进行这种优化:观察到一个代码路径具有未定义的行为意味着编译器可能会得出从未使用过代码路径的结论。

对于观察者来说,给short int 分配过大的值似乎会导致执行完全不同的代码。

【讨论】:

以上是关于大于 32767 的短整型值的主要内容,如果未能解决你的问题,请参考以下文章

工业物联网网关变量的数据类型

C语言里怎样理解长整型 短整型 和无符号型变量和常量?

C语言基本整型的数据范围啥意思

Java 基础数据类型

有符号和无符号整型数据溢出问题

C++ short/int/long/long long 等数据类型大小