【问题标题】:Initializing large 2-dimensional array to all one value in C++将大型二维数组初始化为C++中的所有一个值
【发布时间】:2019-07-17 02:50:50
【问题描述】:

我想在 C++ 中为所有-1 初始化一个大型二维数组(比如 1000x1000,尽管我想更大)。

如果我的数组是一维的,我知道我可以做到:

int my_array[1000];
memset(my_array, -1, sizeof(my_array));

但是,memset 不允许将一个数组的所有元素初始化为另一个数组。我知道我可以制作一个长度为 1000000 的一维数组,但为了便于阅读,我更喜欢二维数组。在将其初始化为所有0 之后,我也可以循环遍历二维数组以设置值,但是这段代码将在我的程序中运行多次,我不确定这会有多快。实现这一目标的最佳方法是什么?

已编辑以添加最小的可重现示例:

int my_array[1000][1000];
// I want my_array[i][j] to be -1 for each i, j

【问题讨论】:

  • 请提供minimal reproducible example - 声明的二维数组以及您希望如何初始化它。如果您关心速度,可以将 2D 数组存储在 1D 中并适当地进行索引。
  • 循环遍历两个维度。只要你不做任何花哨的事情,你的优化器应该优化这段代码就好了。
  • 请注意,1,000,000 ints 将超过许多系统上可用的自动存储。

标签: c++ arrays


【解决方案1】:

我有点惊讶。

我知道,它是 C++。而且,我永远不会使用普通的 C 样式数组。

因此,公认的答案可能是最好的。

但是,如果我们回到这个问题

int my_array[1000];
memset(my_array, -1, sizeof(my_array));

和

int my_array[1000][1000];
// I want my_array[i][j] to be -1 for each i, j

那么最简单最快的解决方法就是和原来的假设一样:

int my_array[1000][1000];
memset(my_array, -1, sizeof(my_array));

没有区别。编译器甚至会优化它并使用快速汇编循环指令。

Sizeof 足够聪明。它会成功的。并且内存是连续的:所以,它会起作用。快速地。很简单。

(一个好的编译器会对其他解决方案进行相同的优化)。

请考虑。

【讨论】:

    【解决方案2】:

    使用 GNU GCC,您可以:

    int my_array[1000][1000] = { [0 .. 999] = { [0 .. 999] = -1, }, };
    

    使用任何其他编译器,您需要:

    int my_array[1000][1000] = { { -1, -1, -1, .. repeat -1 1000 times }, ... repeat { } 1000 times ... };
    

    旁注:下面是做赋值,不是初始化:

    int my_array[1000][1000];
    for (auto&& i : my_array) 
        for (auto&& j : i)
            j = -1;
    

    做你写的和做 for(int i=0; i

    这取决于。如果你有一个糟糕的编译器,你编译时没有优化等等,那么是的。很可能,没有。无论如何,不​​要使用索引。我相信在这种情况下range based for loop 大致翻译成这样:

    for (int (*i)[1000]  = my_array; i < my_array + 1000; ++i)
       for (int *j = *i; j < *i + 1000; ++j)
            *j = -1;
    

    旁注:啊!在每个循环中计算 my_array + 1000 和 *i + 1000 很痛苦。这就像每个循环完成 3 次操作。这cpu时间浪费了!它可以很容易地优化为:

    for (int (*i)[1000]  = my_array, (*max_i)[1000] = my_array + 10000; i < max_i; ++i)
       for (int *j = *i, *max_j = *i + 1000; j < max_j; ++j)
            *j = -1;
    

    循环中使用的my_array[i][j] 转换为*(*(my_array + i) + j)(请参阅aarray subscript operator)。来自指针算术的结果等于*(*((uintptr_t)my_array + i * sizeof(int**)) + j * sizeof(int*))。计数操作,my_array[i][j] 在幕后进行乘法、加法、取消引用、乘法、加法、取消引用 - 类似六种操作。 (当使用不良或非优化编译器时),您的版本可能会慢很多。

    也就是说,一个好的编译器应该将每个版本优化为相同的代码,如here所示。

    其中任何一个都比仅仅通过键入一百万个 -1 显式初始化它要慢得多吗?

    我相信分配每个数组元素(在这种具有易于优化类型int 的元素的特殊情况下)将与初始化一样快或慢。这实际上取决于您的特定编译器和您的架构。一个糟糕的编译器可以执行非常慢的迭代数组元素的版本,所以它会花很长时间。另一方面,静态初始化可以将值嵌入程序中,因此您的程序大小将增加sizeof(int) * 1000 * 1000,并且在程序启动期间为您的程序初始化静态区域时将执行简单的memcpy。因此,与带有分配的适当优化循环相比,您不会在速度和大量只读内存方面一无所获。

    【讨论】:

    • 谢谢。我以前从未见过auto。做你写的和做for(int i=0; i&lt;1000; i++){ for(int j=0; j&lt;1000; j++){ my_array[i][j]=-1; } }之间有什么真正的区别吗?并且这些中的任何一个都比仅仅通过键入一百万个-1 来显式初始化它要慢得多吗?
    【解决方案3】:

    如果数组是静态的,它将被放置在顺序内存中(检查this question)。所以 char [1000][1000] 等于 char [1000000] (如果你的堆栈可以容纳那么多)。

    如果数组是用多维 new(比如char(*x)[5] = new char[5][5])创建的,那么它也是连续的。

    如果不是(如果您使用动态分配创建它),那么您可以使用在 my question 中找到的解决方案在您对它进行 memset 后将它映射到单个数组。

    【讨论】:

    • 如果数组是静态的,它是放在顺序内存中的,这是什么意思。?所有数组都在连续内存中。
    • 他可能意味着像int *array[1000] 这样的可能性,其中每个值都被初始化为另一个数组。
    • @NathanOliver,当你有一个动态多维数组时,你不能把它放在连续的内存中。 char** a; a = new char[10] ; for(int i = 0 ; i &lt; 10 ; i++) a[i] = new char[100]; - 不是连续记忆。
    • 真的保证char[10][10]是连续的吗?
    • @MichaelChourdakis 关于您的更新:char** x = new char[5][5] 不合法。 new char[5][5] 返回 char(*)[5],而不是 char**。
    猜你喜欢
    • 2012-04-06
    • 2016-12-09
    • 1970-01-01
    • 1970-01-01
    • 2021-04-04
    • 1970-01-01
    • 2023-04-11
    • 2017-03-08
    • 1970-01-01
    相关资源
    最近更新 更多