为什么C中有无符号和有符号的int类型?

为什么C中有无符号和有符号的int类型?,c,programming-languages,twos-complement,unsigned-integer,language-concepts,C,Programming Languages,Twos Complement,Unsigned Integer,Language Concepts,我是C语言的初学者。我最近学习了2的补码和其他表示负数的方法,以及为什么2的补码最合适 我想问的是,比如 int a = -3; unsigned int b = -3; //This is the interesting Part. 现在,对于int类型的转换 标准上说: 6.3.1.3有符号和无符号整数 当整数类型的值转换为除_Bool以外的其他整数类型时,如果 该值可以由新类型表示,但保持不变 否则,如果新类型是无符号的,则通过重复添加或 比新类型中可以表示的最大值多减去一个 直到值在新

我是C语言的初学者。我最近学习了
2的补码
和其他表示负数的方法,以及为什么
2的补码
最合适

我想问的是,比如

int a = -3;
unsigned int b = -3; //This is the interesting Part.
现在,对于int类型的转换

标准上说:

6.3.1.3有符号和无符号整数

当整数类型的值转换为除_Bool以外的其他整数类型时,如果 该值可以由新类型表示,但保持不变

否则,如果新类型是无符号的,则通过重复添加或 比新类型中可以表示的最大值多减去一个 直到值在新类型的范围内

第一段不能用作
-3
不能用
无符号int
表示

因此,第2段开始起作用,我们需要知道无符号int的最大值。它可以在limits.h中找到UINT\u MAX。这种情况下的最大值为
4294967295
,因此计算为:

-3 + UINT_MAX + 1 = -3 + 4294967295 + 1 = 4294967293  
现在二进制中的
4294967293
111111111111111111111111111111 01
,而2的补码形式中的
-3
111111111111111111111111 01
,所以它们基本上是相同的位表示,无论我试图将哪个负整数赋给无符号int,它都是相同的。所以无符号类型不是多余的

现在我知道,根据标准,
printf(“%d”,b)
是一种未定义的行为,但这不是一种合理且更直观的做事方式吗。如果负数表示为
2的补码
,那么表示将是相同的,这就是我们现在所拥有的,并且使用的其他方法很少,并且很可能不会在未来的开发中使用


因此,如果我们只能有一种类型,比如int,那么现在如果
intx=-1
,那么
%d
检查符号位,如果符号位是
1
%u
则打印负数,始终按原样解释普通二进制数字(位)。由于使用了
2的补码
,因此已经处理了加法和减法。因此,这不是一种更直观、更简单的做事方式吗。

同时具备输入、输出和计算功能非常方便。例如,比较和除法分为有符号和无符号两种(顺便说一句,在位级,无符号和2的补码有符号类型的乘法是相同的,就像加法和减法一样,两者可以编译成CPU的相同乘法指令)。此外,在溢出的情况下,无符号操作不会导致未定义的行为(除被零除外),而有符号操作会。总的来说,无符号算术定义良好,无符号类型只有一种表示形式(与有符号类型的三种不同表示形式不同,尽管目前实际上只有一种表示形式)

有一个有趣的转折。现代C/C++编译器利用签名溢出导致未定义行为这一事实。逻辑是它永远不会发生,因此可以进行一些额外的优化。如果它真的发生了,标准说这是未定义的行为,你的错误程序在法律上是错误的。这意味着您应该避免签名溢出和所有其他形式的UB。然而,有时您可以仔细编写代码,这些代码不会产生UB,但使用有符号算术比使用无符号算术更有效


请研究未定义、未指定和实现定义的行为。它们都列在标准末尾的一个附件(J?)中。

我的答案更抽象,在我看来,在C语言中,你不应该关心整数在内存中的表示。C语言把这个抽象给你,这很好

将整数声明为无符号是非常有用的。这假设该值永远不会为负值。与浮点数句柄实数一样,
有符号
整数句柄。。。整数和无符号整数处理自然数

当您创建负整数将导致未定义行为的算法时。可以确保无符号整数值永远不会为负。例如,当您迭代数组的索引时。负索引将导致未定义的行为

另一件事是当您创建一个公共API时,当您的一个函数需要一个大小、长度、权重或任何在负值中没有意义的东西时。这有助于用户理解此值的用途


另一方面,有些人不同意,因为
unsigned
的算法并不像人们最初期望的那样有效。因为当一个
无符号的
等于零时,它将递减,它将传递到一个非常大的值。有些人期望他能与
-1
相等。例如:

// wrong
for (size_t i = n - 1; i >= 0; i--) {
  // important stuff
}
这会产生一个无限循环,甚至更糟的是,如果n等于零,编译器可能会检测到它,但不是一直检测到:

// wrong
size_t min = 0;
for (size_t i = n - 1; i >= min; i--) {
  // important stuff
}
使用无符号整数执行此操作需要一些技巧:

size_t i = n;
while (i-- > 0) {
  // important stuff
}


在我看来,在语言中有无符号整数是非常重要的,没有无符号整数C是不完整的。

我认为一个主要原因是运算符和运算取决于有符号性。

您已经观察到,如果有符号类型使用2的补语,则有符号类型和无符号类型的add/subtract行为相同(并且您忽略了这样一个事实,即“if”有时并非如此)

在许多情况下,编译器需要有符号的信息来理解程序的目的

1。整数促销。

当较窄的类型转换为较宽的类型时,编译器将根据操作数的类型生成代码

例如,如果将
带符号的短字符
转换为
带符号的整数
和<
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

int main(void) {
    // your code goes here
    int32_t a=-1;
    int32_t b=-1;
    int64_t c = (int64_t)a * b;
    printf("signed: 0x%016"PRIx64"\n", (uint64_t)c);

    uint32_t d=(uint32_t)-1;
    uint32_t e=(uint32_t)-1;
    uint64_t f = (uint64_t)d * e;
    printf("unsigned: 0x%016"PRIx64"\n", f);

    return 0;
}