代码之家  ›  专栏  ›  技术社区  ›  Michas

C中数组索引的正确类型是什么?

  •  31
  • Michas  · 技术社区  · 14 年前

    我找到了5个候选人:

    • size_t
    • ptrdiff_t
    • intptr_t / uintptr_t
    • int_fast*_t / uint_fast*_t
    • int_least*_t uint_least*_t

    有简单的代码可以更好地说明这个问题。什么样的最好 i j 在这两个特殊的循环中。如果有充分的理由,两种不同的类型也可以。

    for (i=0; i<imax; i++) {
            do_something(a[i]);
    }
    /* jmin can be less than 0 */
    for (j=jmin; j<jmax; j++) {
            do_something(a[j]);
    }
    

    附笔。 在问题的第一个版本中,我忘记了负指数。

    P.P.S。 我不打算写C99编译器。然而,任何来自编译器程序员的回答对我来说都是非常有价值的。

    类似问题:

    8 回复  |  直到 6 年前
        1
  •  29
  •   R.. GitHub STOP HELPING ICE    14 年前

    我几乎总是用 size_t 用于数组索引/循环计数器。当然,在某些特殊情况下,您可能需要有符号偏移量,但通常使用有符号类型有很多问题:

    最大的风险是,如果调用者将内容视为未签名(或者从错误信任的文件中读取内容)而向您传递了一个巨大的大小/偏移量,您可能会将其解释为负数,并且无法发现它超出了界限。例如 if (offset<size) array[offset]=foo; else error();

    使用无符号算术的另一个原因(通常)-有时我使用索引作为位数组的偏移量,我想使用%8和/8或%32和/32。对于有符号类型,这些将是实际的除法运算。对于unsigned,可以生成预期的按位和/位移位操作。

        2
  •  41
  •   Johannes Schaub - litb    5 年前

    我想你应该用 ptrdiff_t 原因如下

    • size_t ,不合适。
    • p2 - p1 . 如果 i == p2 - p1 p2 退后 p2 == p1 + i . 注意 *(p + i) 相当于 p[i]
    • 作为此“常规索引类型”的另一个指示,当内置 operator[] (例如,在指针上)与用户提供的 运算符[] (例如vector's)就是这样吗( http://eel.is/c++draft/over.built#16 &燃气轮机;

      对于每一个cv-qualified或cv-unqualified对象类型T,都存在表单的候选运算符函数

      T*      operator+(T*, std::ptrdiff_t);
      T&      operator[](T*, std::ptrdiff_t);
      T*      operator-(T*, std::ptrdiff_t);
      T*      operator+(std::ptrdiff_t, T*);
      T&      operator[](std::ptrdiff_t, T*);
      

    真正地 大数组或指向一个非常大的内存部分的指针,那么我的“常规索引类型”就不会剪切它,因为这样就不能保证你可以从最后一个元素的地址中减去第一个元素的地址@那么应该用西罗的回答 https://stackoverflow.com/a/31090426/34509 . 就我个人而言,我尽量避免使用无符号类型,因为它们不能表示负边缘情况(例如,在向后迭代时循环结束值),但这是一种宗教辩论(不过,在这一阵营中,我并不孤单)。在需要使用无符号类型的情况下,我当然必须把我的信仰放在一边。

        3
  •  15
  •   Amnon    14 年前

    sizeof(array) (和 malloc 的论点)是 size_t 大小\u t 可用于数组的索引。

    此分析针对基于0的数组,这是常见的情况。 ptrdiff_t 在任何情况下都可以工作,但是索引变量有一个指针差异类型有点奇怪。

        4
  •  10
  •   Ciro Santilli OurBigBook.com    5 年前

    size_t

    如果你从 0 大小\u t 因为该类型必须能够索引任何数组:

    • sizeof 返回它,因此数组的
    • malloc 把它当作论点,如阿姆农所说

    如果从零以下开始,则切换到从零开始,并使用 大小\u t ,因为上述原因,它是可以保证工作的。因此,请更换:

    for (j = jmin; j < jmax; j++) {
        do_something(a[j]);
    }
    

    使用:

    int *b = &a[jmin];
    for (size_t i = 0; i < (jmax - jmin); i++) {
        do_something(b[i]);
    }
    

    为什么? 使用方法:

    • ptrdiff\ :表示的最大值可能小于 大小\u t

      提到这一点 at cppref ,如果数组太大,则可能出现未定义的行为,建议参见C99 6.5.5/9:

      当两个指针相减时,两个指针都指向同一数组对象的元素, 或者超过数组对象的最后一个元素;结果就是 两个数组元素的下标。结果的大小由实现定义, 如果结果不能在该类型的对象中表示,则行为是未定义的 .

      出于好奇, intptr_t 大小\u t 在分段内存体系结构上: https://stackoverflow.com/a/1464194/895245

      GCC还对静态数组对象的最大大小施加了进一步的限制: What is the maximum size of an array in C?

    • UINTPTRUT公司 :我不确定。所以我就用 因为我更确定:-)

        5
  •  4
  •   user3708067    6 年前

    我的选择: ptrdiff\

    许多人投了赞成票 ptrdiff_t ,但有人说,使用指针差分类型进行索引很奇怪。对我来说,这很有意义:数组索引是与原点指针的差。

    size_t 是正确的,因为这是设计来保持大小。但是,正如一些人所评论的:这是以字节为单位的大小,因此通常可以保存比最大可能数组索引大几倍的值。

        6
  •  4
  •   osvein    5 年前

    unsigned int . (虽然我更喜欢速记 unsigned )

    无符号整型 保证能够索引任何可移植阵列。只保证支持65'535字节或更小的数组,最大 无符号整型

    来自C99标准的公开WG14 N1256草案:

    5.2.4.1平移限制

    (...)

    5.2.4.2数值限制

    需要一个实现来记录本子条款中指定的所有限制,这些限制在标题中指定 <limits.h> <float.h> . 附加限制在 <stdint.h>

    5.2.4.2.1整型尺寸 <极限h>

    以下给出的值应替换为适用于 #if 预处理指令。此外,除了 CHAR_BIT MB_LEN_MAX ,下列表达式应替换为与根据整数升迁转换的相应类型的对象的表达式具有相同类型的表达式。它们的实现定义值应等于或大于量级(绝对v

    (...)

    • 无符号整型 UINT_MAX 65535 // 2^16 - 1

    在C89中,最大的可移植数组大小实际上只有32'767字节,所以即使是有符号的 int 最大值至少为32'767(附录a.4)。

    从§C89草案的2.2.4:

    2.2.4.1平移限制

    (...)

    • 对象中的32767字节(仅在托管环境中)

    2.2.4.2数值限制

    一致性实施应记录本节规定的所有限制,这些限制应在标题中规定 <极限h> <浮动。h> .

    “整型尺寸 < "

    以下给出的值应替换为适用于#if预处理指令的常量表达式。它们的实现定义值的大小(绝对值)应等于或大于所示值,并带有相同的符号。

    (...)

    • int类型对象的最大值 INT_MAX +32767
        7
  •  2
  •   codymanix    14 年前

    如果事先知道数组的最大长度,可以使用

    • int_fast*_t / uint_fast*_t
    • int_least*_t / uint_least*_t

    在所有其他情况下,我建议使用

    • size_t

    • ptrdiff_t

    根据天气情况,允许使用负索引。

    • intptr_t / uintptr_t

        8
  •  2
  •   Diogenes Creosote    9 年前

    在你的情况下,我会用 ptrdiff_t . 不仅仅是指标可以是负的。您可能希望倒计时到零,在这种情况下,有符号类型会产生一个讨厌的、微妙的bug:

    for(size_t i=5; i>=0; i--) {
      printf("danger, this loops forever\n);
    }
    

    如果你使用 ptrdiff\ 或任何其他合适的签名类型。在POSIX系统上,可以使用 ssize_t

    就我个人而言,我经常 int ,尽管这可能不是正确的做法。

        9
  •  1
  •   S.S. Anne    5 年前

    我通常使用 size_t 对于数组偏移量,但如果要负数组索引,请使用 int . 它能够寻址C89保证的最大大小的数组(32767字节)。

    如果要访问C99保证的最大大小(65535字节)的数组,请使用 unsigned

    请参阅以前的版本,了解C允许但不保证访问的数组。