14

参考本指南:
https ://google.github.io/styleguide/cppguide.html#Integer_Types

谷歌建议int在大部分时间使用。
我尝试遵循本指南,唯一的问题是 STL 容器。


示例 1。

void setElement(int index, int value)
{
    if (index > someExternalVector.size()) return;
    ...
}

比较index.size()正在生成警告。

示例 2。

for (int i = 0; i < someExternalVector.size(); ++i)
{
    ...
}

i和之间的相同警告.size()


如果我声明indexor ias unsigned int,则警告关闭,但类型声明会传播,然后我必须声明更多变量 as unsigned int,然后它与指南相矛盾并失去一致性。

我能想到的最好的方法是使用这样的演员:

if (index > static_cast<int>(someExternalVector.size())

或者

for (int i = 0; i < static_cast<int>(someExternalVector.size()); ++i)

但我真的不喜欢演员阵容。

有什么建议吗?


以下是一些详细的想法:

仅使用有符号整数的好处是:我可以避免有符号/无符号警告、强制转换,并确保每个值都可以是负数(保持一致),因此 -1 可用于表示无效值。

在很多情况下,循环计数器的使用与其他一些常量或结构成员混合使用。因此,如果有符号/无符号不一致,就会有问题。将充满警告和铸件。

4

2 回答 2

12

无符号类型具有三个特征,其中之一在质量上是“好”的,其中一个在质量上是“坏”的:

  • 它们可以容纳两倍于相同大小的有符号类型的值(好)
  • 版本(即 32 位机器上的size_t32 位、64 位机器上的 64 位等)对于表示内存(地址、大小等)很有用(中性)
  • 它们在 0 以下换行,因此在循环中减去 1 或使用 -1 表示无效索引可能会导致错误(不好)。 有符号类型也会换行。

由于上面的前两点,STL 使用无符号类型:为了不限制类数组类的潜在大小,例如vectorand deque(尽管您必须质疑数据结构中需要 4294967296 个元素的频率);因为负值永远不会成为大多数数据结构的有效索引;因为size_t是用于表示与内存有关的任何事情的正确类型,例如结构的大小,以及相关的事物,例如字符串的长度(见下文。)这不一定是将它用于索引或其他内容的好理由非内存用途,例如循环变量。在 C++ 中这样做是最佳实践的原因是一种反向构造,因为它是容器以及其他方法中使用的内容,并且一旦使用,其余代码必须匹配以避免遇到相同的问题。

当值可能变为负数时,您应该使用有符号类型。

当值不能变为负数时,您应该使用无符号类型(可能与“不应该”不同。)

您应该size_t在处理内存大小时使用(sizeof通常是字符串长度等的结果)。它通常被选为默认的无符号类型,因为它与编译代码的平台相匹配。例如,字符串的长度是size_t因为字符串只能有 0 个或多个元素,没有理由将字符串的长度方法任意限制为比平台上可以表示的长度更短,例如 16 位长度(0-65535) 在 32 位平台上。注意(感谢评论者Morwenstd::intptr_tstd::uintptr_t概念上相似的 - 将始终是适合您平台的大小 - 如果您想要不是指针的东西,则应将其用于内存地址。注 2(感谢评论者 rubenvbsize_t-1)由于 的值,字符串只能保存元素npos。详情如下。

这意味着如果您使用 -1 来表示无效值,则应该使用有符号整数。如果您使用循环向后迭代数据,如果您不确定循环构造是否正确,则应考虑使用有符号整数(并且如其他答案之一所述,它们很容易出错。)IMO ,您不应该诉诸技巧来确保代码有效 - 如果代码需要技巧,那通常是一个危险信号。此外,对于那些关注您并阅读您的代码的人来说,这将更难理解。这两个都是不遵循上述@Jasmin Gray 回答的原因。

迭代器

然而,在 C++ 中使用基于整数的循环来迭代数据结构的内容是错误的,因此在某种意义上,关于有符号与无符号 for 循环的争论是没有意义的。您应该改用迭代器:

std::vector<foo> bar;
for (std::vector<foo>::const_iterator it = bar.begin(); it != bar.end(); ++it) {
  // Access using *it or it->, e.g.:
  const foo & a = *it;

当你这样做时,你不需要担心演员表、签名等。

迭代器可以向前(如上)或反向,用于向后迭代。使用相同的语法it != bar.end(),因为end()表示迭代的结束,而不是底层概念数组、树或其他结构的结束。

换句话说,您的问题“在使用 STL 容器时应该使用 int 还是 unsigned int?”的答案。是'也不是。改用迭代器。 阅读更多关于:

还剩下什么?

如果循环不使用整数类型,还剩下什么?您自己的值,这取决于您的数据,但在您的情况下包括使用 -1 作为无效值。这很简单。使用签名。只要保持一致。

我非常相信使用自然类型,例如枚举,并且有符号整数适合这一点。它们更符合我们的概念预期。当您的思想和代码保持一致时,您编写错误代码的可能性就会降低,而更有可能写出正确、干净的代码。

于 2013-06-20T07:21:41.240 回答
4

使用容器返回的类型。在这种情况下,size_t - 这是一个无符号整数类型。(从技术上讲,它是std::vector<MyType>::size_type,但通常定义为 size_t,因此使用 size_t 是安全的。无符号也可以)

但总的来说,为正确的工作使用正确的工具。“指数”是否应该为负数?如果没有,请不要签名。

顺便说一句,您不必输入“unsigned int”。'unsigned' 是相同变量类型的简写:

int myVar1;
unsigned myVar2;

原始问题中链接到的页面说:

有些人,包括一些教科书作者,建议使用无符号类型来表示从不为负的数字。这是一种自我记录的形式。然而,在 C 语言中,此类文档的优势被它可能引入的真正错误所抵消。

这不仅仅是自我记录,而是使用正确的工具完成正确的工作。说“无符号变量会导致错误,所以不要使用无符号变量”是愚蠢的。有符号变量也可能导致错误。浮点数也可以(超过整数)。唯一保证没有错误的代码是不存在的代码。

他们为什么 unsigned 是邪恶的例子是这个循环:

for (unsigned int i = foo.Length()-1; i >= 0; --i)

我很难在循环中向后迭代,而且我通常会犯错误(有符号或无符号整数)。我要从尺寸中减去一个吗?我是让它大于等于 0,还是仅仅大于?这是一个草率的情况开始。

那么你如何处理你知道你有问题的代码呢?你改变你的编码风格来解决问题,让它更简单,更容易阅读,更容易记住。他们发布的循环中有一个错误。错误是,他们希望允许低于零的值,但他们选择使其无符号。这是他们的错。

但这里有一个简单的技巧,可以让阅读、记忆、书写和运行变得更容易。带有无符号变量。这是明智的做法(显然,这是我的观点)。

for(unsigned i = myContainer.size(); i--> 0; )
{
    std::cout << myContainer[i] << std::endl;
}

它是未签名的。它总是有效的。对起始尺寸没有负面影响。不用担心下溢。它只是工作。这很聪明。做对了,不要停止使用无符号变量,因为有人曾经说过他们在 for() 循环中犯了错误,并且未能训练自己不犯错误

记住它的诀窍:

  1. 将“i”设置为大小。(不用担心减一)
  2. 使“i”像箭头一样指向0。i --> 0(它是后递减 (i--) 和大于比较 (i > 0) 的组合)

最好自学正确编码的技巧,然后因为你没有正确编码而丢弃工具。

你想在你的代码中看到哪个?

for(unsigned i = myContainer.size()-1; i >= 0; --i)

或者:

for(unsigned i = myContainer.size(); i--> 0; )

不是因为输入的字符更少(那会很愚蠢),而是因为它减少了精神上的混乱。浏览代码时更容易进行心理解析,也更容易发现错误。


自己试试代码

于 2013-06-19T03:56:30.617 回答