为什么将 "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
不会保持负值(或更准确 - 它不会关心符号 -)。很明显 scores
和 grades
是无符号值(没有人得分 -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
结果,这意味着 2
和 1
之间的差是 4294967295
.
最大值的两倍
int
使用一位来表示值的符号。 unsigned
将此位用作另一个数字位。所以通常,int
有 31 个数字位,而 unsigned
有 32 个。这个额外的位通常被引用为使用 unsigned
的原因。但是如果 31 位不足以满足特定目的,那么 32 位很可能也不够,您应该考虑 64 位或更多位。
函数重载
从 int
到 unsigned
的隐式转换与从 int
到 double
的转换具有相同的秩,因此以下示例格式错误:
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 <= 100
是 true
。
我是 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
不会保持负值(或更准确 - 它不会关心符号 -)。很明显 scores
和 grades
是无符号值(没有人得分 -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
结果,这意味着 2
和 1
之间的差是 4294967295
.
最大值的两倍
int
使用一位来表示值的符号。 unsigned
将此位用作另一个数字位。所以通常,int
有 31 个数字位,而 unsigned
有 32 个。这个额外的位通常被引用为使用 unsigned
的原因。但是如果 31 位不足以满足特定目的,那么 32 位很可能也不够,您应该考虑 64 位或更多位。
函数重载
从 int
到 unsigned
的隐式转换与从 int
到 double
的转换具有相同的秩,因此以下示例格式错误:
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 <= 100
是 true
。