C语言 在C中将二维数组归零的最快方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2516096/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 04:56:51  来源:igfitidea点击:

Fastest way to zero out a 2d array in C?

carraysmultidimensional-arrayzeromemset

提问by Eddy

I want to repeatedly zero a large 2d array in C. This is what I do at the moment:

我想在 C 中反复将一个大型二维数组归零。这就是我目前所做的:

// Array of size n * m, where n may not equal m
for(j = 0; j < n; j++)
{
    for(i = 0; i < m; i++)
    {  
        array[i][j] = 0;
    }
}

I've tried using memset:

我试过使用 memset:

memset(array, 0, sizeof(array))

But this only works for 1D arrays. When I printf the contents of the 2D array, the first row is zeroes, but then I got a load of random large numbers and it crashes.

但这仅适用于一维数组。当我打印二维数组的内容时,第一行是零,但随后我得到了大量随机大数并且它崩溃了。

回答by James McNellis

memset(array, 0, sizeof(array[0][0]) * m * n);

Where mand nare the width and height of the two-dimensional array (in your example, you have a square two-dimensional array, so m == n).

其中mn是二维数组的宽度和高度(在您的示例中,您有一个方形二维数组,所以m == n)。

回答by Alok Singhal

If arrayis truly an array, then you can "zero it out" with:

如果array确实是一个数组,那么您可以使用以下命令“将其归零”:

memset(array, 0, sizeof array);

But there are two points you should know:

但是有两点你应该知道:

  • this works only if arrayis really a "two-d array", i.e., was declared T array[M][N];for some type T.
  • it works only in the scope where arraywas declared. If you pass it to a function, then the name arraydecays to a pointer, and sizeofwill not give you the size of the array.
  • 这仅在array确实是“二维数组”时才有效,即为T array[M][N];某种类型声明T
  • 它仅在array声明的范围内有效。如果将其传递给函数,则名称array会衰减为指针,并且sizeof不会为您提供数组的大小。

Let's do an experiment:

我们来做一个实验:

#include <stdio.h>

void f(int (*arr)[5])
{
    printf("f:    sizeof arr:       %zu\n", sizeof arr);
    printf("f:    sizeof arr[0]:    %zu\n", sizeof arr[0]);
    printf("f:    sizeof arr[0][0]: %zu\n", sizeof arr[0][0]);
}

int main(void)
{
    int arr[10][5];
    printf("main: sizeof arr:       %zu\n", sizeof arr);
    printf("main: sizeof arr[0]:    %zu\n", sizeof arr[0]);
    printf("main: sizeof arr[0][0]: %zu\n\n", sizeof arr[0][0]);
    f(arr);
    return 0;
}

On my machine, the above prints:

在我的机器上,上面打印:

main: sizeof arr:       200
main: sizeof arr[0]:    20
main: sizeof arr[0][0]: 4

f:    sizeof arr:       8
f:    sizeof arr[0]:    20
f:    sizeof arr[0][0]: 4

Even though arris an array, it decays to a pointer to its first element when passed to f(), and therefore the sizes printed in f()are "wrong". Also, in f()the size of arr[0]is the size of the array arr[0], which is an "array [5] of int". It is not the size of an int *, because the "decaying" only happens at the first level, and that is why we need to declare f()as taking a pointer to an array of the correct size.

即使arr是一个数组,它在传递给 时也会衰减为指向其第一个元素的指针f(),因此打印的大小f()是“错误的”。此外,在f()大小中arr[0]是数组的大小arr[0],它是“数组[5]的int”。它不是 an 的大小int *,因为“衰减”只发生在第一级,这就是为什么我们需要声明f()一个指向正确大小数组的指针。

So, as I said, what you were doing originally will work only if the two conditions above are satisfied. If not, you will need to do what others have said:

所以,正如我所说,只有满足上述两个条件,你原来所做的才有效。如果没有,您将需要按照其他人所说的去做:

memset(array, 0, m*n*sizeof array[0][0]);

Finally, memset()and the forloop you posted are not equivalent in the strict sense. There could be (and have been) compilers where "all bits zero" does not equal zero for certain types, such as pointers and floating-point values. I doubt that you need to worry about that though.

最后,您发布memset()for循环在严格意义上并不等效。可能(并且曾经)编译器对于某些类型(例如指针和浮点值)“所有位为零”不等于零。我怀疑您是否需要担心这一点。

回答by Skizz

Well, the fastest way to do it is to not do it at all.

好吧,最快的方法是根本不做。

Sounds odd I know, here's some pseudocode:

我知道这听起来很奇怪,这是一些伪代码:

int array [][];
bool array_is_empty;


void ClearArray ()
{
   array_is_empty = true;
}

int ReadValue (int x, int y)
{
   return array_is_empty ? 0 : array [x][y];
}

void SetValue (int x, int y, int value)
{
   if (array_is_empty)
   {
      memset (array, 0, number of byte the array uses);
      array_is_empty = false;
   }
   array [x][y] = value;
}

Actually, it's still clearing the array, but only when something is being written to the array. This isn't a big advantage here. However, if the 2D array was implemented using, say, a quad tree (not a dynamic one mind), or a collection of rows of data, then you can localise the effect of the boolean flag, but you'd need more flags. In the quad tree just set the empty flag for the root node, in the array of rows just set the flag for each row.

实际上,它仍在清除数组,但仅当有内容写入数组时。这在这里不是一个很大的优势。但是,如果二维数组是使用四叉树(不是动态思维)或数据行集合实现的,那么您可以本地化布尔标志的效果,但您需要更多标志。在四叉树中,只需为根节点设置空标志,在行数组中只需为每一行设置标志。

Which leads to the question "why do you want to repeatedly zero a large 2d array"? What is the array used for? Is there a way to change the code so that the array doesn't need zeroing?

这导致了“为什么要重复将大型二维数组归零”的问题?数组是干什么用的?有没有办法更改代码以便数组不需要归零?

For example, if you had:

例如,如果您有:

clear array
for each set of data
  for each element in data set
    array += element 

that is, use it for an accumulation buffer, then changing it like this would improve the performance no end:

也就是说,将它用于累积缓冲区,然后像这样更改它会无休止地提高性能:

 for set 0 and set 1
   for each element in each set
     array = element1 + element2

 for remaining data sets
   for each element in data set
     array += element 

This doesn't require the array to be cleared but still works. And that will be far faster than clearing the array. Like I said, the fastest way is to not do it in the first place.

这不需要清除数组,但仍然有效。这将比清除数组快得多。就像我说的,最快的方法是一开始就不做。

回答by Jarrod Smith

If you are really, really obsessed with speed (and not so much with portability) I think the absolute fastestway to do this would be to use SIMD vector intrinsics. e.g. on Intel CPUs, you could use these SSE2 instructions:

如果您真的非常着迷于速度(而不是可移植性),我认为绝对最快的方法是使用 SIMD 向量内在函数。例如,在 Intel CPU 上,您可以使用这些 SSE2 指令:

__m128i _mm_setzero_si128 ();                   // Create a quadword with a value of 0.
void _mm_storeu_si128 (__m128i *p, __m128i a);  // Write a quadword to the specified address.

Each store instruction will set four 32-bit ints to zero in one hit.

每条存储指令都会在一次命中中将四个 32 位整数设置为零。

p must be 16-byte aligned, but this restriction is also good for speed because it will help the cache. The other restriction is that p must point to an allocation size that is a multiple of 16-bytes, but this is cool too because it allows us to unroll the loop easily.

p 必须是 16 字节对齐的,但是这个限制也有利于速度,因为它有助于缓存。另一个限制是 p 必须指向一个 16 字节倍数的分配大小,但这也很酷,因为它允许我们轻松展开循环。

Have this in a loop, and unroll the loop a few times, and you will have a crazy fast initialiser:

将它放在一个循环中,并展开循环几次,您将拥有一个疯狂的快速初始化程序:

// Assumes int is 32-bits.
const int mr = roundUpToNearestMultiple(m, 4);      // This isn't the optimal modification of m and n, but done this way here for clarity.    
const int nr = roundUpToNearestMultiple(n, 4);    

int i = 0;
int array[mr][nr] __attribute__ ((aligned (16)));   // GCC directive.
__m128i* px = (__m128i*)array;
const int incr = s >> 2;                            // Unroll it 4 times.
const __m128i zero128 = _mm_setzero_si128();

for(i = 0; i < s; i += incr)
{
    _mm_storeu_si128(px++, zero128);
    _mm_storeu_si128(px++, zero128);
    _mm_storeu_si128(px++, zero128);
    _mm_storeu_si128(px++, zero128);
}

There is also a variant of _mm_storeuthat bypasses the cache (i.e. zeroing the array won't pollute the cache) which could give you some secondary performance benefits in some circumstances.

还有一种_mm_storeu绕过缓存的变体(即,将数组清零不会污染缓存),这可以在某些情况下为您带来一些次要的性能优势。

See here for SSE2 reference: http://msdn.microsoft.com/en-us/library/kcwz153a(v=vs.80).aspx

有关 SSE2 参考,请参见此处:http: //msdn.microsoft.com/en-us/library/kcwz153a(v=vs.80) .aspx

回答by Ben Zotto

If you initialize the array with malloc, use callocinstead; it will zero your array for free. (Same perf obviously as memset, just less code for you.)

如果您使用 初始化数组malloc,请calloc改用;它将免费将您的数组归零。(显然与 memset 的性能相同,只是为您减少了代码。)

回答by Engineer

int array[N][M] = {0};

int array[N][M] = {0};

...at least in GCC 4.8.

...至少在 GCC 4.8 中。

回答by Pablo Santa Cruz

How was your 2D array declared?

你的二维数组是如何声明的?

If it something like:

如果它是这样的:

int arr[20][30];

You can zero it by doing:

您可以通过执行以下操作将其归零:

memset(arr, sizeof(int)*20*30);

回答by DUDE_MXP

Use calloc instead of malloc . calloc will initiate all fields to 0.

使用 calloc 而不是 malloc 。calloc 会将所有字段初始化为 0。

int *a = (int *)calloc(n,size of(int)) ;

int *a = (int *)calloc(n,size of(int)) ;

//all cells of a have been initialized to 0

//a的所有单元格都被初始化为0

回答by swestrup

memset(array, 0, sizeof(int [n][n]));

回答by C?lin Calin

You can try this

你可以试试这个

int array[20,30] = {{0}};