【问题标题】:Best practices for handling variable size arrays in c / c++?在 C / C++ 中处理可变大小数组的最佳实践?
【发布时间】:2010-09-16 10:27:37
【问题描述】:

如果我有一个固定大小的数组,具体取决于它的定义和使用方式,我通常使用两种方法之一来引用它。

数组类型 1:由于它是基于定义的固定大小,我只是在所有引用它的循环中使用该定义。

#define MAXPLAYERS 4

int playerscores[MAXPLAYERS];

for(i=0;i<MAXPLAYERS;++i)
{
.... do something with each player
}

数组类型 2:由于这个数组可以随着项目的增加而增长,所以我使用 sizeof 来计算其中的条目数。编译器会将大小转换为常量,因此这样做不会有任何运行时损失。

typedef struct
{
    fields....
}MYSTRUCT_DEF;

MYSTRUCT_DEF mystruct[]={
   {entry 1},
   {entry 2},
   {entry 3...n}
   };

 for(i=0;i<(sizeof(mystruct)/sizeof(MYSTRUCT_DEF));++i)
 {
 ..... do something with each entry
 }

有没有更优雅的解决方案来处理数组而不超过结尾或过早停止。想法?评论?

【问题讨论】:

  • 请注意,这不是通常意义上的可变大小数组(其中元素的数量在运行时会发生变化)。
  • 如果您使用的是 C++,那么您确实应该使用其中一种 STL 容器。否则删除 C++ 标记。虽然 C++ 和 C 是半可移植的,但它们并不是一回事。

标签: c++ c arrays


【解决方案1】:

无论数组元素类型如何,这都适用于您的两种情况:

#define ARRAY_COUNT(x) (sizeof(x)/sizeof((x)[0]))

...

struct foo arr[100];
...

for (i = 0; i < ARRAY_COUNT(arr); ++i) {
    /* do stuff to arr[i] */
}

【讨论】:

  • 而且,即使在第一种情况下它只是一个固定大小的 int 数组,它也可以工作。通过使用 ARRAY_COUNT 宏,您可以避免更改数组大小(通过使用不同的#define)但忘记更改所有循环的问题。
  • 要非常小心,不要将指针传递给 ARRAY_COUNT,因为那样你会得到错误的答案。
【解决方案2】:

在 C++ 中只需使用向量类。

如果由于某种原因你不能,那么有你想要的宏实现。 请参阅以下答案,了解 winnt.h 中的一组宏,这些宏在 C 中工作,在 C++ 中更安全:

Can this macro be converted to a function?

【讨论】:

    【解决方案3】:

    使用stdlib.h的_countof宏

    From this MSDN article:

    // crt_countof.cpp
    #define _UNICODE
    #include <stdio.h>
    #include <stdlib.h>
    #include <tchar.h>
    int main( void )
    {
       _TCHAR arr[20], *p;
       printf( "sizeof(arr) = %d bytes\n", sizeof(arr) );
       printf( "_countof(arr) = %d elements\n", _countof(arr) );
       // In C++, the following line would generate a compile-time error:
       // printf( "%d\n", _countof(p) ); // error C2784 (because p is a pointer)
    
       _tcscpy_s( arr, _countof(arr), _T("a string") );
       // unlike sizeof, _countof works here for both narrow- and wide-character strings
    }
    

    【讨论】:

    • 当然,前面的下划线告诉您它的便携性。如果您只需要针对单个编译器,那很好,但如果您需要移植到其他编译器(即使不是其他平台),它可能会在以后给您带来麻烦。
    • 实际上 - 我只是想偷它来放入我现有的宏中。我喜欢 sizeof() 的对称性。
    • 这是一个不错的 - 不是真的很便携,但仍然有点
    【解决方案4】:

    C 代码很常见

    struct foo {
        ...  /* fields */
    };
    struct foo array[] = {
        { ... }, /* item 1 */
        { ... }, /* item 2 */
        ...,
        { 0 } /* terminator */
    };
    for (i = 0; array[i].some_field; i++) {
        ...
    }
    

    对于普通元素,您通常可以找到至少一个永远不会为 0/NULL 的字段,如果不是,您可以使用其他一些特殊的 END 值。

    在我编写的代码中,任何涉及编译时大小的数组的事情都是通过 Checkers 的答案中的 ARRAY_COUNT 之类的宏来完成的,并且运行时大小的数组总是带有一个大小计数器,位于数组的结构中。

    struct array_of_stuff {
        struct stuff *array;
        int count;   /* number of used elements */
        int length;  /* number of elements allocated */
    };
    

    length 字段允许轻松批量调整大小。

    【讨论】:

      【解决方案5】:

      对于 C,我建议 realloc 动态引入新变量。如果您正在静态地做某事,我建议您使用#define。我不确定我是否会称之为最佳实践,但是,今天,我就是这样实践的。

      C++ 最佳实践是使用 stl::vector。 A reference here

      【讨论】:

        【解决方案6】:

        我几乎总是使用包装类(MFC CArray、stl 矢量等),除非有特殊原因。没有太多开销,你会得到很多调试检查,你可以动态调整大小,获取大小很容易等等。

        【讨论】:

          【解决方案7】:

          对于 C++,使用 std::vector

          使用 C 数组没有实际意义。 std::vector 具有(几乎)与 C 数组相同的性能,它将:

          • 按需增长
          • 知道它的大小
          • 验证您确实访问了正确的内存(即,如果您超出其界限,它可能会引发异常)

          这甚至没有考虑与 std::vector 相关的通用算法。

          现在,使用 C

          你至少可以通过两种方式写得更好一些。首先,将定义替换为真正的常量变量:

          // #define MAXPLAYERS 4
          const unsigned int MAXPLAYERS = 4 ;
          
          int playerscores[MAXPLAYERS];
          
          for(i=0;i<MAXPLAYERS;++i)
          {
          .... do something with each player
          }
          

          使用真正的变量会为您提供更多的类型安全性,并且不会污染全局范围。为了最小化依赖,你甚至可以在头文件中声明变量,并在源代码中定义它们:

          /* header.h */
          extern const unsigned int MAXPLAYERS ;
          extern int playerscores[] ;
          
          /* source.c */
          const unsigned int MAXPLAYERS = 4
          int playerscores[MAXPLAYERS];
          
          /* another_source.c */
          #include "header.h"
          
          for(i=0;i<MAXPLAYERS;++i)
          {
          .... do something with each player
          }
          

          这样,您将能够在一个源中更改数组的大小,而无需重新编译使用它的所有源。缺点是 MAXPLAYERS 在编译时不再为人所知(但是,这真的是缺点吗?)

          请注意,您的第二种类型的数组不能动态增长。 sizeof(至少在 C++ 中)在编译时进行评估。对于不断增长的数组,malloc/realloc/free 是 C 的方式,而 std::vector(或任何其他通用 STL 容器)是 C++ 的方式。

          【讨论】:

            【解决方案8】:

            请务必阅读this question's answers - 许多可移植的数组大小问题的解决方案。

            我特别喜欢 _countof(参见 Brian R. Bondy 的回答)——普利策奖是这个名字的发明者!

            【讨论】:

              【解决方案9】:

              到目前为止,如果您在 C++ 中使用 T[] 数组,则除了答案之外: 使用模板参数推导来推导数组大小。更安全:

              template&lt;int N&gt; void for_all_objects(MYSTRUCT_DEF[N] myobjects)

              如果您将 mystruct 更改为 malloc'ed/new'ed MYSTRUCT_DEF*,您的 sizeof(mystruct)/sizeof(MYSTRUCT_DEF) 表达式会默默地失败。 sizeof(mystruct) 然后变成sizeof(MYSTRUCT_DEF*),它通常小于sizeof(MYSTRUCT_DEF),你的循环计数为0。看起来代码根本没有执行,这可能很令人困惑。上面的模板声明会给你一个明确的编译器错误(“mystruct is not an array”)

              【讨论】:

                猜你喜欢
                • 2012-03-05
                • 1970-01-01
                • 2011-12-23
                • 1970-01-01
                • 2013-08-07
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多