【问题标题】:For iterating though an array should we be using size_t or ptrdiff_t?对于遍历数组,我们应该使用 size_t 还是 ptrdiff_t?
【发布时间】:2018-12-16 01:13:42
【问题描述】:

在这个blog entry by Andrey Karpov entitled, "About size_t and ptrdiff_t"他展示了一个例子,

for (ptrdiff_t i = 0; i < n; i++)
  a[i] = 0;

但是,我不确定这是否正确,似乎应该是

for (size_t i = 0; i < n; i++)
  a[i] = 0;

这对吗?

我知道我们也应该使用memset 之类的东西,但让我们完全避免这种情况。我只是问类型

【问题讨论】:

  • ptrdiff_t 是带符号的类型,所以如果你用它访问数组可能不是很安全。
  • 取决于n 是什么以及a 是什么。如果a是内置数组,size_t更合适。
  • 不确定 container::size_type 是什么,但这个问题是关于 C 的,而不是 C++。(猜猜那是 C++?)
  • 链接的文章完全是假的,充满了错误和误解。你当然是对的。忘掉那篇文章和写这篇文章的人吧。
  • 在这种特殊情况下,i 的所有值都 >=0,因此 size_t 肯定更合适。

标签: c pointers size-t errata ptrdiff-t


【解决方案1】:

a blog post 中,我认为您应该始终避免分配大于 PTRDIFF_MAX(*) 的内存块,因为这样做会使 Clang 和 GCC 等编译器生成无意义的代码,即使您不减去指向该块以导致结果溢出的方式。

(*) 即使malloc 在您传递一个大于PTRDIFF_MAX 的值时成功。问题的症结在于 GCC 和 Clang 只生成与这样的 malloc 链接时行为正确的代码,但 Glibc 提供了一个 malloc 函数,没有实现这个限制。

如果您遵循该约束(我鼓励您这样做:这是博客文章的信息),那么这两种类型都同样正确。

也就是说,由于只需要表示正偏移量,size_t 将是您示例中的自然选择。

【讨论】:

  • 这是一个有趣的皱纹,我一直幸福地没有意识到。我从来没有负责构建一个应用程序,该应用程序需要一个大小为 size_t 范围一半的单一分配。我确信它们在外面,但我认为它们很少见。
  • @jwdonahue:也许你会对Extra, Extra — Read All About It: Nearly All Binary Searches and Mergesorts Are Broken 感兴趣——一篇来自 Google 的关于数组如何变得足够大以至于索引算法确实溢出和中断的帖子。
  • PTRDIFF_MAX 不是最大值。任何对象的允许大小,但最大。 ptrdiff_t 的值。 SIZE_MAX 只是最大值。 size_t 可以表示的值。没有与最大值有关。分配大小。这将通过静态/自动对象的实现和动态分配对象的标准库(或环境提供的任何机制,如果有的话)来保证。尽管如此,size_t 保证保持最大值。允许的数组索引和最大可能对象的大小(以字节为单位)。 ptrdiff_t 实际上不是(另见 6.5.6p9)。
  • @Olaf 确认 glibc 允许在 32 位平台上分配超过 2GiB,这与编译器假设相矛盾,同时决定不更改:gcc.gnu.org/bugzilla/show_bug.cgi?id=67999#c8
  • @Olaf 你读过博文或错误报告吗?它们表明 GCC 和 Clang 为定义的程序生成错误代码。错误报告包含一个讨论,涉及 GCC 维护者和 libc 维护者,讨论应该在编译器和 libc 之间修复什么。您的评论没有在讨论中添加任何内容。
【解决方案2】:

使用ptrdiff_t 是可以的,因为a[i] 被翻译为*(a + i)

如果您采用两个指针,p1p2,建议您使用:

ptrdiff_t d = p2 - p1; // Assuming p2 - p1 is valid.

鉴于,p2 == p1 + d,即&lt;ptr type&gt; + ptrdiff_t 是一个有效的表达式。 ptrdiff_tsize_t 是否更好,因为索引类型是团队中使用的意见和/或编码风格的问题。

【讨论】:

  • 我不相信这个证明是正确的。将类型和变量放在一起会令人困惑。问题是关于类型的:当然是#=#-# 所以#+#=#。但在我们的例子中,类型是有界的。例如,在上面的示例中,您以 ptrdiff_t = uintptr_t - uintptr_t 开头。 not 保证可以工作,即使在同一个对象上也不行。 ptrdiff_t + uintptr_t = uintptr_t 也更不可能工作。关于值而不是类型的问题,如果任何构造都可以正常工作,那么您是对的(afaik),但是在这个问题中,我们正在创建内存访问器。
  • re: a[i] = 0; 我的论点是因为 i 可以是两种类型(在问题中,size_tptrdiff_tptrdiff_t 可以大于 size_t 和否定的是,离开size_t 的类型安全性较差。但这听起来不像编码风格或意见。
  • @EvanCarroll,只要数组大小小于PTRDIFF_MAX。您可以使用ptrdiff_tsize_t,程序的行为没有任何区别。当您的数组大小超过PTRDIFF_MAX 时,使用size_t 是您唯一的选择。听起来这就是你想说的。对于这种极端情况,我可以看到编码风格或意见并不重要。
【解决方案3】:

OP 的问题的答案是肯定的,size_t 最适合示例代码,其中没有指针值相互减去,并且malloc 行为周围没有交叉编译器/库兼容性问题。不管堆管理器有何不同,在 C 中,数组的长度可以是 SIZE_MAX 字节,这需要 size_t 来表示它。标准中的任何内容都不需要堆管理器能够分配堆中的所有进程内存空间,或者为此分配最多 SIZE_MAX 字节,但数组的长度可以是 SIZE_MAX,因此 @987654326 @是合适的。

即使n 已签名,使用ptrdiff_t 代替i 也无济于事,因为如果n 为负数,初始i &lt; n 测试无论如何都会失败,因为i 是初始化为零。在i 中没有索引是size_t 无法访问的。唯一需要ptrdiff_t 的地方是从另一个指针值中减去一个指针值,而 OP 并没有询问这一点。

【讨论】:

    猜你喜欢
    • 2015-07-28
    • 2015-11-02
    • 2012-11-06
    • 2018-11-23
    • 2016-04-10
    • 1970-01-01
    • 1970-01-01
    • 2011-07-02
    • 1970-01-01
    相关资源
    最近更新 更多