代码之家  ›  专栏  ›  技术社区  ›  J Collins

在数组迭代器中使用size_t

  •  14
  • J Collins  · 技术社区  · 12 年前

    我最近了解到,引入size_t是为了帮助未来的代码抵御本地位计数的增加和可用内存的增加。具体的用途定义似乎是存储某个东西的大小,通常是一个数组。

    我现在必须想知道,这种未来的证明应该走多远。如果下一个迭代数组的任务使用例如 unsigned int 作为索引数组:

    void (double* vector, size_t vectorLength) {
        for (unsigned int i = 0; i < vectorLength; i++) {
            //...
        }
    }
    

    事实上,在这种情况下,我可能希望语法严格地将无符号int上转换为关系运算符的size_t。

    这是否意味着迭代器变量 i 应该只是一个 size_t ?

    这是否意味着任何程序中的任何整数都必须在功能上被标识为是否将被用作数组索引?

    这是否意味着任何使用逻辑以编程方式开发索引的代码都应该创建一个size_t类型的新结果值,特别是如果逻辑依赖于可能有符号的整数值?即

    double foo[100];
    //...
    int a = 4;
    int b = -10;
    int c = 50;
    
    int index = a + b + c;
    double d = foo[(size_t)index];
    

    当然,由于我的代码逻辑创建了一个固定绑定,向上转换为size_t不会提供额外的保护。

    5 回复  |  直到 12 年前
        1
  •  14
  •   Shahbaz Martin York    12 年前

    你应该记住语言的自动转换规则。

    这是否意味着迭代器变量i应该是size_t?

    是的,因为如果 size_t 大于 unsigned int 并且您的数组实际上比使用 无符号整型 ,然后是变量( i )永远无法达到阵列的大小。

    这是否意味着任何程序中的任何整数都必须在功能上被标识为是否将被用作数组索引?

    你试图让它听起来很激烈,但事实并非如此。为什么选择变量作为 double 而不是 float ? 为什么要将变量设为 unsigned 一个没有?你为什么要做一个变量 short 而另一个是 int ? 当然,你总是知道你的变量将用于什么,所以你决定它们应该得到什么类型。选择 大小(_t) 是众多中的一个,也是同样的决定。

    换句话说, 每一个 程序中的变量应在功能上进行标识,并给出正确的类型。

    这是否意味着任何使用逻辑以编程方式开发索引的代码都应该创建一个size_t类型的新结果值,特别是如果逻辑依赖于可能有符号的整数值?

    一点也不。首先,如果变量永远不能有负值,那么它可能是 无符号整型 大小(_t) 首先。第二,如果变量 可以 如果在计算过程中有负值,那么您应该确定最后它是非负值,因为您不应该用负数索引数组。

    也就是说,如果你确定你的指数是非负的,那么把它转换成 大小(_t) 没有任何区别。C11在6.5.2.1中表示(强调我的):

    后缀表达式,后跟方括号中的表达式 [] 是下标的 数组对象元素的指定。 下标运算符的定义 [] 是吗 E1[E2] 与相同 (*((E1)+(E2))) 。由于适用于二进制+运算符的转换规则,如果 E1 是数组对象(相当于指向数组对象的初始元素的指针),并且 E2 是整数, E1[E2] 指定 第2页 的元素 第1页 (从零开始计数)。

    这意味着任何类型的 index 为此 some_pointer + index 这是有意义的,允许用作索引。换句话说,如果你知道 整数 有足够的空间来包含正在计算的索引,因此绝对不需要将其转换为其他类型。

        2
  •  5
  •   Lundin    12 年前

    如果下一个迭代数组的任务使用无符号整数作为索引数组,那么使用未来证明和适当大小的size_t定义数组长度无疑是毫无意义的

    是的。所以不要这样做。

    事实上,在这种情况下,我可能希望语法严格地将无符号int上转换为关系运算符的size_t。

    它只会在这方面得到推广 < 活动int变量的上限不会更改,因此++操作将始终使用int而不是size_t。

    这是否意味着迭代器变量i应该是size_t?

    这是否意味着任何程序中的任何整数都必须在功能上被标识为是否将被用作数组索引?

    是的,它比int好……但有一种更聪明的方法来编写程序: 使用常识 。每当您声明数组时,实际上可以停止并 提前考虑 阵列可能需要存储多少项。如果它永远不会包含超过100个项目,那么您绝对没有理由使用 int 也不能使用 size_t 为其编制索引。

    在100个项目的情况下,只需使用 uint_fast8_t 。然后,该程序针对大小和速度进行了优化,并且100%可移植。

    无论何时声明一个变量,一个好的程序员都会激活他们的大脑,并考虑以下事项:

    • 我将在这个变量中存储的值的范围是什么?
    • 我真的需要在里面存储负数吗?
    • 在数组的情况下,最坏情况下我需要多少个值?(如果未知,是否必须使用动态内存?)
    • 如果我决定移植此程序,此变量是否存在兼容性问题?

    与坏程序员不同,坏程序员不会激活大脑,只会打字 整数 到处都是。

        3
  •  3
  •   Community Mohan Dere    8 年前

    Neil Kirk ,迭代器是 size_t .

    问题中的另一点是位置的计算,这通常包括 完全的 位置(例如。 a 在您的示例中),可能还有一个或多个 相对的 数量(例如。 b c ),可能 签署 .

    签署的副本 大小(_t) ptrdiff_t 和迭代器类型类似 I typename I::difference_type .

    正如您在问题中所描述的,最好在代码中的任何地方使用适当的类型,这样就不需要转换。为了提高内存效率,如果您在其他阵列中有一个一百万个位置的阵列,并且您知道这些位置在0-255之间,那么您可以使用 unsigned char ; 但随后在某个时刻需要转换。

    在这种情况下,最好 名称 这种类型,例如。

    using pos = unsigned char;
    

    并使所有转换显式。如果未来0-255范围增加,代码将更容易维护。

        4
  •  1
  •   Neil Kirk    12 年前

    是的,如果使用int对数组进行索引,则会在其他地方使用size_t。这就是为什么可以在STL中使用迭代器。它们是未来的证明。对于C数组,可以使用size_t、指针、算法和基于lambda或范围的For循环(C++11)。如果您需要将大小或索引存储在变量中,那么它们将需要是size_t或其他适当的类型,以及它们与之交互的任何其他类型,除非您知道大小会很小。(例如,如果存储的两个元素之间的距离始终在一个小范围内,则可以使用int)。

    double *my_array;
    for (double *it = my_array, *end_it = my_array + my_array_size, it != end_it; ++it)
    {
        // use it
    }
    
    std::for_each(std::begin(my_array), std::end(my_array), [](double& x)
    {
        // use x
    });
    
    for (auto& x : my_array)
    {
        // use x
    }
    
        5
  •  1
  •   glglgl John Dvorak    12 年前

    这是否意味着任何程序中的任何整数都必须在功能上被标识为是否将被用作数组索引?

    我会选择这一点,然后说清楚 此外,在大多数情况下,用作数组索引的变量仅用作该变量(或与之相关的变量)。

    这条规则不仅适用于这里,也适用于其他情况:现在有很多用例中存在一种特殊类型: ptrdiff_t , off_t (这甚至可能根据我们使用的配置而改变!), pid_t 还有很多其他人。