在C中将2d数组归零的最快方法?

时间:2010-03-25 13:57:21

标签: c arrays multidimensional-array zero memset

我想在C中反复归零一个大的2d数组。这就是我现在所做的:

// 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;
    }
}

我尝试过使用memset:

memset(array, 0, sizeof(array))

但这仅适用于1D阵列。当我打印2D阵列的内容时,第一行是零,但随后我得到了一大堆随机大数字,它崩溃了。

12 个答案:

答案 0 :(得分:161)

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

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

答案 1 :(得分:72)

如果array确实是一个数组,那么你可以用“

”将其“清零”
memset(array, 0, sizeof array);

但是你应该知道两点:

  • 仅当array确实是“二维数组”时才有效,即某些类型T array[M][N];被声明为T
  • 它仅适用于声明array的范围。如果您将其传递给某个函数,则名称array decays to a pointersizeof将不会为您提供该数组的大小。

我们来做一个实验:

#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;
}

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

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

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

所以,正如我所说,只有满足上述两个条件,你原来所做的才会奏效。如果没有,你需要做别人说的话:

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

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

答案 2 :(得分:9)

嗯,最快的方法就是不要这样做。

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

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;
}

实际上,它仍在清除阵列,但只有在向阵列写入某些内容时。这不是一个很大的优势。但是,如果使用四叉树(不是动态的一个头脑)或一组数据行来实现2D数组,那么您可以本地化布尔标志的效果,但是您需要更多的标志。在四叉树中,只需为根节点设置空标志,在行数组中为每一行设置标志。

这引出了一个问题“为什么要反复归零大型2d阵列”?用于什么数组?有没有办法更改代码,以便数组不需要归零?

例如,如果你有:

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

也就是说,将它用于累积缓冲区,然后像这样改变它将改善性能无止境:

 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 

这不需要清除数组但仍然有效。这比清除阵列要快得多。就像我说的那样,最快的方法是首先不要这样做。

答案 3 :(得分:7)

如果你真的非常痴迷于速度(而不是可移植性),我认为绝对最快的方法是使用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.

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

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

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

// 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);
}

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

请参阅此处获取SSE2参考:http://msdn.microsoft.com/en-us/library/kcwz153a(v=vs.80).aspx

答案 4 :(得分:5)

如果使用malloc初始化数组,请改用calloc;它将免费归零您的阵列。 (显然与memset相同,只需要更少的代码。)

答案 5 :(得分:3)

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

......至少在GCC 4.8中。

答案 6 :(得分:2)

您的2D数组是如何声明的?

如果是这样的话:

int arr[20][30];

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

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

答案 7 :(得分:1)

使用calloc而不是malloc。 calloc将所有字段都启动为0。

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

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

答案 8 :(得分:0)

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

答案 9 :(得分:0)

我认为手动执行此操作的最快方法是遵循代码。你可以将它的速度与memset函数进行比较,但它不应该慢。

(如果你的数组类型不同,那么改变ptr和ptr1指针的类型)


#define SIZE_X 100
#define SIZE_Y 100

int *ptr, *ptr1;
ptr = &array[0][0];
ptr1 = ptr + SIZE_X*SIZE_Y*sizeof(array[0][0]);

while(ptr < ptr1)
{
    *ptr++ = 0;
}

答案 10 :(得分:0)

你可以试试这个

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

答案 11 :(得分:-2)

这是因为sizeof(array)为您提供了 array 指向的对象的分配大小。 ( array 只是指向多维数组第一行的指针)。但是,您分配了大小为 i j 数组。因此,您需要将sizeof(数组)返回的一行的大小乘以您分配的行数,例如:

bzero(array, sizeof(array) * j);

另请注意,sizeof(数组)仅适用于静态分配的数组。对于动态分配的数组,您可以编写

size_t arrayByteSize = sizeof(int) * i * j; 
int *array = malloc(array2dByteSite);
bzero(array, arrayByteSize);