简体   繁体   English

C如何以有符号和无符号整数存储负数?

[英]How does C store negative numbers in signed vs unsigned integers?

Here is the example: 这是示例:

#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;
}

Output: 输出:

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

Does it really matter if I declare the value as signed or unsigned int? 我将值声明为有符号还是无符号int真的有关系吗? Because, C actually only cares about how I read the value from memory. 因为,C实际上只关心我如何从内存中读取值。 Please help me understand this and I hope you prove me wrong. 请帮助我理解这一点,希望您证明我错了。

An unsigned int and a signed int take up the same number of bytes in memory. 一个unsigned int和一个有signed int占用内存中相同数量的字节。 They can store the same byte values. 它们可以存储相同的字节值。 However the data will be treated differently depending on if it's signed or unsigned. 但是,根据数据是签名还是未签名,将对数据进行不同的处理。

See http://en.wikipedia.org/wiki/Two%27s_complement for an explanation of the most common way to represent integer values. 有关表示整数值的最常见方法的说明,请参见http://en.wikipedia.org/wiki/Two%27s_complement

Since you can typecast in C you can effectively force the compiler to treat an unsigned int as signed int and vice versa, but beware that it doesn't mean it will do what you think or that the representation will be correct. 由于可以在C中进行类型转换,因此可以有效地强制编译器将无符号的int视为带符号的int,反之亦然,但是请注意,这并不意味着它将按照您的想法进行操作或表示正确。 (Overflowing a signed integer invokes undefined behaviour in C). (溢出有符号整数会调用C中的未定义行为)。

(As pointed out in comments, there are other ways to represent integers than two's complement, however two's complement is the most common way on desktop machines.) (如注释中所指出的,除了二进制补码以外,还有其他表示整数的方法,但是在台式机上,二进制补码是最常见的方式。)

Does it really matter if I declare the value as signed or unsigned int? 我将值声明为有符号还是无符号int真的有关系吗?

Yes. 是。

For example, have a look at 例如看一下

#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);
}

and its output 及其输出

1.333333
1.000000
-0.000000
-1431655764.000000

which clearly shows that it makes a huge difference if I have the same byte representation interpreted as signed or unsigned. 这清楚地表明,如果我将相同的字节表示形式解释为有符号或无符号,那将有很大的不同。

Representation of signed integers is up to the underlying platform, not the C language itself. 有符号整数的表示取决于基础平台,而不是C语言本身。 The language definition is mostly agnostic with regard to signed integer representations. 语言定义在大多数情况下与带符号整数表示形式无关。 Two's complement is probably the most common, but there are other representations such as one's complement and signed magnitude . 二进制补码可能是最常见的,但是还有其他表示形式,例如一个补码和有符号的幅度

In a two's complement system, you negate a value by inverting the bits and adding 1. To get from 5 to -5 , you'd do: 在二进制补码系统中,通过反转位并加1来取反值。要从5-5 ,您需要执行以下操作:

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

To go from -5 back to 5 , you follow the same procedure: 要从-5返回到5 ,请遵循相同的过程:

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

Does it really matter if I declare the value as signed or unsigned int? 我将值声明为有符号还是无符号int真的有关系吗?

Yes, for the following reasons: 是的,原因如下:

  1. It affects the values you can represent: unsigned integers can represent values from 0 to 2 N -1 , whereas signed integers can represent values between -2 N-1 and 2 N-1 -1 (two's complement). 它会影响您可以表示的值:无符号整数可以表示02 N -1 ,而有符号整数可以表示-2 N-12 N-1 -1 (二进制补码)之间的值。

  2. Overflow is well-defined for unsigned integers; 溢出是为无符号整数定义的; UINT_MAX + 1 will "wrap" back to 0 . UINT_MAX + 1将“包装”回0 Overflow is not well-defined for signed integers, and INT_MAX + 1 may "wrap" to INT_MIN , or it may not. 对于带符号整数,溢出定义明确,并且INT_MAX + 1 可能 “包装”到INT_MIN ,也可能不是。

  3. Because of 1 and 2, it affects arithmetic results, especially if you mix signed and unsigned variables in the same expression (in which case the result may not be well defined if there's an overflow). 由于1和2,它会影响算术结果,尤其是在同一表达式中混合有符号和无符号变量时(在这种情况下,如果发生溢出,则可能无法很好地定义结果)。

#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);
}

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

the int number store is not different, it stored with Complement style in memory, int号码存储区没有什么不同,它以补数形式存储在内存中,

I can use 0X... the 35 in 0X00000023, and the -35 in 0Xffffffdd, it is not different you use sigend or unsigend. 我可以使用0X ... 0X00000023中的35,以及0Xffffffdd中的-35,使用sigend或unsigend并没有什么不同。 it only output with different sytle. 它只输出不同的sytle。 The %d and %u is not different about positive, but the negative the first position is sign, if you output with %u is 0Xffffffdd equal 4294967261, but the %d the 0Xffffffdd can be - 0X00000023 equal -35. %d和%u在正数上没有区别,但是负数的第一个位置是正负号,如果您输出的%u是0Xffffffdd等于4294967261,但%d的0Xffffffdd可以是-0X00000023等于-35。

The most fundamental thing that variable's type defines is the way it is stored (that is - read from and written to) in memory and how are the bits interpreted, so your statement can be considered "valid". 变量类型定义的最基本的事情是它在内存中的存储方式(即读取和写入)以及位的解释方式,因此您的语句可以被视为“有效”。

You can also look at the problem using conversions. 您也可以使用转化查看问题。 When you store signed and negative value in unsigned variable it gets converted to unsigned. 当您将有符号和负值存储在无符号变量中时,它将转换为无符号。 It so happens that this conversion is reversible, so signed -35 converts to unsigned 4294967261, which - when you request it - can be converted to signed -35. 碰巧这种转换是可逆的,因此带符号的-35转换为无符号的4294967261,当您请求时,可以将其转换为带符号的-35。 That's how 2's complement encoding (see link in other answer) works. 这就是2的补码编码(参见其他答案中的链接)的工作方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM