【问题标题】:Using a size_t as limiter for a "for loop"使用 size_t 作为“for 循环”的限制器
【发布时间】:2016-05-13 08:14:07
【问题描述】:

我在我的 s6 上使用名为 CppDroid 的 C++ 应用程序来制作一个快速程序。

如何使用 size_t 作为计数器上“for 循环”的限制器?

int c;

//... more codes here...

    for (c=0; c < a.used; ++c)

        //... more codes here...

a.used 是一些使用过的数组,它们来自一个解决方案,用于制作dynamic sized array

错误是:不同符号整数的比较:'int'和'size_t'(又名无符号整数)

for 循环是程序的内部嵌套循环之一,因此我想尽可能将变量 c 保持为“int”。

我看过关于 Comparing int with size_t 的示例,但我不确定它有什么帮助,因为它是针对“如果”条件的。

【问题讨论】:

  • 使用std::size_t c; 而不是int c; ?
  • 无论如何,这是一个警告,而不是错误(除非您要求将警告视为错误,这可能是明智的)。解决此问题的一个好方法是引入一个 n_items 函数,该函数将动态大小作为有符号整数返回,例如ptrdiff_t。使用 typedef Size 会有所帮助。
  • 您仍然可以使用 cast 进行投射。
  • interjay 这是 ++c ty,已编辑

标签: c++


【解决方案1】:

只需使用std::size_t c 而不是int c

【讨论】:

  • 这在技术上可以工作,关闭编译器,但它引入了无符号算术和转换。这往往会引入错误。所以这不是一个好主意。
  • @Cheersandhth.-Alf:除了a.used 已经是size_t,你必须假设它可以取大部分(如果不是全部)可能的值。无论如何,你都有unsigned 算术。如果您想偏执,请验证循环计数器的 a.used != SIZE_MAX, but using size_t` 是确保安全和正确的唯一方法,否则您可能会提前终止循环(如果 (int)a.used导致换行)或从不终止(如果a.used &gt; INT_MAX,可能是UINT_MAX,那么c 在达到循环终止条件之前会换行)。
  • @Cheersandhth.-Alf 在典型的现代 GNU/Linux 系统 (x86_64) 上,int 有 32 位,size_t 有 64 位,并且可以分配超过 2GB 甚至 4GB .你认为这是一个非典型的系统吗?我不。它不会是最常用的环境,但它仍然很常见,值得担心。
  • @Cheersandhth.-Alf 当循环变量表示数组索引时,您为循环变量使用int 辩护说这在现代编程中不是问题。我举了一个现代系统的例子。所以是的,一个int 不够的例子。对我来说似乎足够相关。我不明白你为什么要把ptrdiff_t 带进来。
  • @Cheersandhth.-Alf 您的答案使用int 表示cn,而没有检查您要存储在其中的值是否合适。明天我将很高兴构建一个简单的程序来准确地展示这将如何在我的系统上失败。 (请原谅我之前对您上一条评论的回复,我没有仔细阅读。现在重写,重点是。)“除非 OP 的原始代码有问题。”:是的。
【解决方案2】:

只要a.used在迭代过程中不发生变化,一个常见的习语是:

for(int c=0, n=a.used; c<n; ++c) {
    ...
}

通过这种方式,强制转换是隐式发生的,并且循环体中还有“元素总数”变量n。此外,当 n 来自方法调用(例如,vec.size())时,您只需对其进行一次评估,这样效率会更高。1


1. 理论上编译器可以自己做这个优化,但是对于像std::vector这样的“复杂”的东西和一个不平凡的循环体,证明它是一个循环是非常困难的不变的,所以通常它只是在每次迭代时重新计算。

【讨论】:

  • 赞成手动优化并解释为什么编译器并不总是可能这样做。好点子。
  • 你能把 var i 改成 c,因为它是使用的 var。
【解决方案3】:

关于

比较不同符号的整数:'int'和'size_t'(又名无符号整数)

…这是一个警告。这不是阻止创建可执行文件的错误,除非您已要求编译器将警告视为错误。

解决它的一个非常直接的方法是使用强制转换,int(a.size)

更一般地说,我建议定义一个通用函数来做到这一点,例如命名为n_items(C++17 将有一个size 函数,不幸的是,结果是无符号的,并且合并了两个或多个逻辑函数,因此采用了这个名称):

using My_array = ...; // Whatever

using Size = ptrdiff_t;

auto n_items( My_array const& a )
    -> Size
{ return a.used; }

然后为你的循环:

for( int c = 0; c < n_items( a ); ++c )

顺便说一下,重用一个变量通常不是一个好主意™,比如这里的c。我假设重用是无意的。上面的例子展示了如何在for循环头中声明循环变量。


另外,正如Matteo Italiahis answer 中指出的那样,如果测量 表明它是一个瓶颈,那么手动优化这样的循环有时可能是个好主意。这是因为编译器无法轻易证明n_items 调用或任何其他动态数组大小表达式的结果在循环体的所有执行中都是相同的(“不变”)。

因此,如果测量告诉您可能重复的大小表达式评估是一个瓶颈,您可以这样做,例如

for( int c = 0, n = n_items( a ); c < n; ++c )

值得注意的是,任何手动优化都会带来成本,这些成本不容易衡量,但其严重程度足以让通常的建议是推迟优化,直到衡量结果告诉您确实需要它。

【讨论】:

  • 需要明确的是,正如我们昨天谈到的,使用int 将导致具有多个INT_MAX 元素的数组出现问题。我是否正确理解您,您不认为这是代码中的缺陷,而是在这样的数组中传递的代码中的缺陷?无论哪种方式,在我看来,考虑到它的破坏程度,它都值得一个警告。一个简单的例子是this one,因为c 溢出到INT_MIN,导致了越界访问。
  • @hvd:你说得对,如果数组有多个 INT_MAX 项,那么 OP 的代码就有问题。但是,对不起,“传递”对我来说没有意义。循环(特定情况)不是函数,因此“传入”不适合,并且函数(一般情况)不使用int而是ptrdiff_t,因此没有大小限制。
  • 是的,措辞草率,抱歉。 for 循环将出现在函数内部。我的意思是如果该函数传递了一个大数组。 OP 使用的动态数组实现使用size_t 作为长度,因此可以传入如此大的数组。
  • @hvd:嗯,你不知道,这个数组正好有 16 个项目,十六进制数字。 :) 或其他类似的东西。我们不知道。 // More codes here 注释可能隐藏了一个严重错误,如果循环体周围有大括号,就不会发生这种错误。嗯。
  • 是的。我们不知道的事实告诉我,我们不应该假设长度适合int。对你来说,这表明我们不应该假设我们需要担心特殊情况,对吧?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-18
  • 2017-04-25
  • 1970-01-01
  • 2015-07-24
  • 2014-03-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多