【问题标题】:How does C store negative numbers in signed vs unsigned integers?C如何在有符号和无符号整数中存储负数?
【发布时间】:2014-10-31 10:57:31
【问题描述】:

示例如下:

#include <stdio.h>

int main()
{
    int x=35;
    int y=-35;
    unsigned int z=35;
    unsigned int p=-35;
    signed int q=-35;
    printf("Int(35d)=%d\n\
Int(-35d)=%d\n\
UInt(35u)=%u\n\
UInt(-35u)=%u\n\
UInt(-35d)=%d\n\
SInt(-35u)=%u\n",x,y,z,p,p,q);

    return 0;
}

输出:

Int(35d)=35
Int(-35d)=-35
UInt(35u)=35
UInt(-35u)=4294967261
UInt(-35d)=-35
SInt(-35u)=4294967261

我将值声明为有符号整数还是无符号整数真的很重要吗?因为,C 实际上只关心我如何从内存中读取值。请帮助我理解这一点,我希望你能证明我错了。

【问题讨论】:

  • 存储数字的不是 C,而是特定的计算机。 C99 标准只是记录了它们行为的一组属性。

标签: c int unsigned-integer


【解决方案1】:

Representation of signed integers 取决于底层平台,而不是 C 语言本身。对于有符号整数表示,语言定义大多是不可知的。 Two's complement 可能是最常见的,但还有其他表示形式,例如 one's complementsigned magnitude

在二进制补码系统中,您通过反转位并加 1 来否定一个值。要从 5-5,您可以:

5 == 0101 => 1010 + 1 == 1011 == -5

要从-5 回到5,请遵循相同的步骤:

-5 == 1011 => 0100 + 1 == 0101 == 5

我将值声明为有符号整数还是无符号整数真的很重要吗?

是的,原因如下:

  1. 它会影响您可以表示的值:无符号整数可以表示从02<sup>N</sup>-1 的值,而有符号整数可以表示介于-2<sup>N-1</sup>2<sup>N-1</sup>-1(二进制补码)之间的值。

  2. 对于无符号整数,溢出是明确定义的; UINT_MAX + 1 将“换行”回0没有为有符号整数明确定义溢出,INT_MAX + 1 可能“换行”到INT_MIN,或者它可能不会。

  3. 因为 1 和 2,它会影响算术结果,尤其是当您在同一个表达式中混合有符号和无符号变量时(在这种情况下,如果出现溢出,结果可能无法很好地定义)。

【讨论】:

    【解决方案2】:

    unsigned intsigned int 在内存中占用相同数量的字节。它们可以存储相同的字节值。但是,数据将根据是否已签名或未签名而被区别对待。

    请参阅http://en.wikipedia.org/wiki/Two%27s_complement,了解表示整数值的最常用方法。

    由于您可以在 C 中进行类型转换,因此您可以有效地强制编译器将 unsigned int 视为有符号 int,反之亦然,但请注意,这并不意味着它会按照您的想法进行或表示是正确的。 (溢出有符号整数会调用 C 中未定义的行为)。

    (正如 cmets 中所指出的,除了二进制补码之外,还有其他表示整数的方法,但是二进制补码是桌面机器上最常见的方式。)

    【讨论】:

    • C 可能会这样做...有时。误导和不完整。
    • 情况很简单,不是 C 怎么做,而是 C 有时会怎么做。其他时候,它以不同的方式做事,可能会产生不同的结果。
    • 一台计算机可能具有整数的补码表示,并具有 C99 标准实现。但这很不寻常!而且 C99 实现甚至不需要任何计算机(您可以使用一堆人类奴隶,但这将是不道德的、低效的、脆弱的!)。
    • 你的推理是错误的。转换为 unsigned 是保值的,如果超出范围,则取模 1+MAX_VALUE。如果在特定情况下这应该与 2s-complement-behavior 相对应,那是巧合。 (C 在溢出时没有 2s-complement-behaviour,它有 UB。)
    • @Deduplicator 我进行了一些编辑,以使其更清楚我的意思。并不是暗示进行转换既不安全也不正确,只是说明可以强制编译器执行某些操作,无论它们有时多么愚蠢。希望现在更清楚以避免混淆。
    【解决方案3】:

    我将值声明为有符号整数还是无符号整数真的很重要吗?

    是的。

    例如,看看

    #include <stdio.h>
    
    int main()
    {
        int a = -4;
        int b = -3;
        unsigned int c = -4;
        unsigned int d = -3;
        printf("%f\n%f\n%f\n%f\n", 1.0 * a/b, 1.0 * c/d, 1.0*a/d, 1.*c/b);
    }
    

    及其输出

    1.333333
    1.000000
    -0.000000
    -1431655764.000000
    

    这清楚地表明,如果我将相同的字节表示解释为有符号或无符号,则会产生巨大的差异。

    【讨论】:

    • 有没有办法读取二进制表示以确切了解那里发生了什么?
    • @Deduplicator 谢谢,你知道 unsigned char 的说明符吗?我通常将 c 用于字符,但我不确定如何读取为无符号。
    • @A6Tech:您将 (int?-) 变量读取为 sizeofunsigned chars 的连续变量。但是将这些组成部分unsigned chars 打印为整数。 (为获得最佳效果,2 位十六进制数:%02X
    • 输出是:FFFFFFFC FFFFFFFD FFFFFFFC FFFFFFFD 所以,没有任何区别。
    • @A6Tech:因为根据表达式是有符号还是无符号,的解释不同。
    【解决方案4】:
    #include <stdio.h>
    
    int main(){
        int x = 35, y = -35;
        unsigned int z = 35, p = -35;
        signed int q = -35;
    
        printf("x=%d\tx=%u\ty=%d\ty=%u\tz=%d\tz=%u\tp=%d\tp=%u\tq=%d\tq=%u\t",x,x,y,y,z,z,p,p,q,q);
    }
    

    结果是: x=35 x=35 y=-35 y=4294967261 z=35 z=35 p=-35 p=4294967261 q=-35 q=4294967261

    int number store 也不一样,它以 Complement 方式存储在内存中,

    我可以使用 0X... 0X00000023 中的 35 和 0Xffffffdd 中的 -35,使用 sigend 或 unsigend 没有区别。它只输出不同的风格。 %d 和 %u 关于正数没有区别,但是负数第一个位置是符号,如果你输出 %u 是 0Xffffffdd 等于 4294967261,但是 %d 0Xffffffdd 可以是 - 0X00000023 等于 -35。

    【讨论】:

    • 这就是为什么我问 unsigned int 的用途是什么......但是 glglgl 的例子表明,读取与 float 相同的二进制数会产生不同的算术结果。但我不明白怎么...
    • glglgl的例子是浮点数除法,那个例子不是int除法,数字以IEEE 754(en.wikipedia.org/wiki/IEEE_754-1985)的方式存储在内存中,浮点除法有点复杂,可以看一些关于信息,也区分符号或非符号。
    【解决方案5】:

    变量类型定义的最基本的东西是它在内存中的存储方式(即读取和写入)以及位是如何解释的,因此您的语句可以被认为是“有效的”。

    您还可以使用转换来查看问题。当您将有符号和负值存储在无符号变量中时,它会转换为无符号。碰巧这种转换是可逆的,因此带符号的 -35 转换为无符号的 4294967261,当您请求时,可以将其转换为带符号的 -35。这就是 2 的补码编码(参见其他答案中的链接)的工作原理。

    【讨论】:

    • 那么,当我可以将所有数字存储在 int 中并按照我的意愿将它们读取为有符号或无符号时,对 unsigned int 有什么需求? :D
    • @A6Tech 如果它有 2 个含义,你怎么知道选择哪个?
    • 您也可以使用原始内存 (void*) 和原始缓冲区并在任何地方进行类型转换,但那是 C,而不是汇编程序 (;
    • 2 的补码不是契约性的。
    猜你喜欢
    • 1970-01-01
    • 2021-11-25
    • 2023-03-17
    • 2015-01-24
    • 2013-10-02
    • 1970-01-01
    • 2015-05-28
    • 2015-02-17
    • 1970-01-01
    相关资源
    最近更新 更多