为什么将 "score[11] = {};" 和 "grade" 声明为 "unsigned" 而不是“int”

why declare "score[11] = {};" and "grade" as "unsigned" instead of "int'

我是 C++ 的新手,正在尝试学习数组的概念。我在网上看到了这段代码片段。对于下面的示例代码,声明有什么区别:

unsigned scores[11] = {};
unsigned grade; 

如:

int scores[11] = {};
int grade; 

我想score[11] = {};grade被声明为unsigned肯定是有原因的,但背后的原因是什么?

int main() {
    unsigned scores[11] = {};
    unsigned grade;
    while (cin >> grade) {
        if (0 <= grade <= 100) {
            ++scores[grade / 10];
        }
    }
    for (int i = 0; i < 11; i++) {
        cout << scores[i] << endl;
    }
}

是的,它确实有所作为。在第一种情况下,您将 11 个元素的数组声明为 "unsigned int" 类型的变量。在第二种情况下,您将它们声明为整数。

当 int 为 32 位时,您可以获得以下范围内的值

–2,147,483,648 到 2,147,483,647 用于普通 int

无符号整数为 0 到 4,294,967,295

当您不需要负数并且需要由 unsigned 提供的额外范围时,您通常会声明一些 unsigned。在您的情况下,我假设通过声明变量无符号,开发人员不接受负分数和成绩。您基本上统计了在命令行中引入了多少个 0 到 10 之间的等级。所以它看起来像是模拟学校评分系统的东西,因此你没有负分。但这是我看完代码后的看法。

看看这个 post,它解释了什么是无符号的:

what is the unsigned datatype?

unsigned 意味着 variable 不会保持负值(或更准确 - 它不会关心符号 -)。很明显 scoresgrades 是无符号值(没有人得分 -25)。所以,自然要用unsigned

但请注意:if (0 <= grade <= 100) 是多余的。 if (grade <= 100) 就足够了,因为不允许使用负值。

正如 Blastfurnace 评论的那样,if (0 <= grade <= 100) 甚至都不对。如果你想要这样,你应该把它写成:

if (0 <= grade && grade <= 100)

无符号变量

将变量声明为 unsigned int 而不是 int 有 2 个结果:

  • 不能为负。它为您提供了永远不会出现的保证,因此您无需检查它并在编写仅适用于正整数的代码时处理特殊情况
  • 由于尺寸有限,因此可以表示更大的数字。在 32 位上,最大的 unsigned int 是 4294967295 (2^32-1) 而最大的 int 是 2147483647 (2^31-1)

使用unsigned int的一个后果是运算将在unsigned int的集合中完成。所以 9 - 10 = 4294967295 而不是 -1 因为没有负数可以在 unsigned int 类型上编码。如果将它们与负 int.

进行比较,您也会遇到问题

More info on how negative integer are encoded.

数组初始化

对于数组定义,如果只写:

unsigned int scores[11];

那么你有 11 个未初始化的 unsigned int,它们的值可能不同于 0。

如果你写:

unsigned int scores[11] = {};

然后所有 int 都使用默认值 0 进行初始化。

注意如果你写:

unsigned int scores[11] = { 1, 2 };

您会将第一个 int 初始化为 1,第二个初始化为 2,所有其他初始化为 0。

您可以轻松地使用所有这些语法来更好地理解它。

比较

关于代码:

if(0 <= grade <= 100)

正如评论中所述,这并没有达到您的预期。事实上,这将始终评估为真,因此执行 if 中的代码。这意味着如果您输入等级,比如 20000,您应该有一个核心转储。原因是这样的:

0 <= grade <= 100

相当于:

(0 <= grade) <= 100

并且第一部分是 true(隐式转换为 1)或 false(隐式转换为 0)。由于两个值都小于 100,因此第二次比较总是 true.

顾名思义,有符号整数可以是负数,而无符号整数则不能。如果我们用 N 位表示一个整数,那么对于无符号整数,最小值为 0,最大值为 2^(N-1)。如果它是 N 位的带符号整数,则它可以取 -2^(N-2) 到 2^(N-2)-1 之间的值。这是因为我们需要 1 位来表示符号 +/-

例如:带符号的 3 位整数(是的,有这样的东西)

000 = 0
001 = 1
010 = 2
011 = 3
100 = -4
101 = -3
110 = -2
111 = -1

但是,对于 unsigned,它只代表值 [0,7]。示例中的最高有效位 (MSB) 表示负值。也就是说,设置 MSB 的所有值都是负数。因此,它的绝对值明显丢失了一点。

它的行为也符合人们的预期。如果你递增 -1 (111) 我们得到 (1 000) 但因为我们没有第四位它只是 "falls off the end" 并且我们剩下 000.

0减1也是一样,先取补码

 111 = twos_complement(001) 

并将其添加到 000,得到 111 = -1(来自 table),这是人们可能期望的。当你增加 011(=3) 产生 100(=-4) 时会发生什么可能不是人们所期望的,并且与我们的正常期望不一致。这些溢出对于定点运算来说很麻烦,必须处理。

另一件值得指出的事情是,有符号整数可以取一个负值,而不是正值,这会导致舍入(例如,当使用整数表示定点数时),但我确信这一点得到了更好的涵盖在 DSP 或信号处理论坛中。

unsigned 整数有一些奇怪的性质,除非你有充分的理由,否则你应该避免使用它们。获得 1 个正大小的额外位,或表达一个值不能为负的约束,是 不是 好的理由。

unsigned 整数实现算术 UINT_MAX+1。相比之下,对 signed 整数的运算代表了我们在学校熟悉的自然算术。

溢出语义

unsigned 有定义明确的溢出; signed 没有:

unsigned u = UINT_MAX;
u++; // u becomes 0
int i = INT_MAX;
i++; // undefined behaviour

这会导致在测试期间可以捕获有符号整数溢出,而无符号溢出可能会默默地做错事。因此,仅当您确定要使溢出合法化时才使用 unsigned

如果您有一个值不能为负的约束,那么您需要一种检测和拒绝负值的方法; int 非常适合这个。 unsigned 将接受负值并默默地将其溢出为正值。

位移语义

unsigned 的位移量不大于数据类型中的位数总是明确定义的。在 C++20 之前,如果 signed 的移位会导致符号位中的 1 左移,则它是未定义的;如果它会导致符号位中的 1 右移,则它是实现定义的。自 C++20 起,signed 右移始终保留符号,但 signed 左移不保留符号。因此,使用 unsigned 进行某些类型的位操作。

混合符号运算

内置算术运算总是对相同类型的操作数进行运算。如果为它们提供不同类型的操作数,“通常的算术转换”会将它们强制转换为同一类型,有时会产生令人惊讶的结果:

unsigned u = 42;
std::cout << (u * -1); // 4294967254
std::cout << std::boolalpha << (u >= -1); // false

有什么区别?

从另一个 unsigned 中减去一个 unsigned 得到一个 unsigned 结果,这意味着 21 之间的差是 4294967295.

最大值的两倍

int 使用一位来表示值的符号。 unsigned 将此位用作另一个数字位。所以通常,int 有 31 个数字位,而 unsigned 有 32 个。这个额外的位通常被引用为使用 unsigned 的原因。但是如果 31 位不足以满足特定目的,那么 32 位很可能也不够,您应该考虑 64 位或更多位。

函数重载

intunsigned 的隐式转换与从 intdouble 的转换具有相同的秩,因此以下示例格式错误:

void f(unsigned);
void f(double);
f(42); // error: ambiguous call to overloaded function

互操作性

许多 API(包括标准库)使用 unsigned 类型,通常是出于误导的原因。在与这些 API 交互时,使用 unsigned 以避免混合符号操作是明智的。

附录

引用的片段包含表达式 0 <= grade <= 100。这将首先评估 0 <= grade,它总是 true,因为 grade 不能为负数。然后它将评估 true <= 100,它始终是 true,因为 true 被转换为整数 1,而 1 <= 100true