100

我想在 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;
    }
}

我试过使用 memset:

memset(array, 0, sizeof(array))

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

4

12 回答 12

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

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

于 2010-03-25T13:59:44.460 回答
85

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

memset(array, 0, sizeof array);

但是有两点你应该知道:

  • 仅当array它确实是“二维数组”时才有效,即为T array[M][N];某种类型声明T
  • 它仅在array声明的范围内有效。如果将其传递给函数,则名称array 会衰减为指针,并且sizeof不会为您提供数组的大小。

让我们做一个实验:

#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()size 中arr[0]是数组的大小arr[0],它是“数组 [5] of int”。它不是 a 的大小int *,因为“衰减”只发生在第一级,这就是为什么我们需要声明f()一个指向正确大小数组的指针。

因此,正如我所说,只有满足上述两个条件,您最初所做的事情才会起作用。如果没有,您将需要按照其他人所说的进行:

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

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

于 2010-03-25T14:48:58.143 回答
11

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

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

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

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

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

例如,如果您有:

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 

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

于 2010-03-25T14:18:21.477 回答
8

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

于 2012-10-23T22:31:38.517 回答
6

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

于 2010-03-25T14:52:19.383 回答
3

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

...至少在 GCC 4.8 中。

于 2015-01-27T17:59:20.627 回答
2

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

如果是这样的:

int arr[20][30];

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

memset(arr, sizeof(int)*20*30);
于 2010-03-25T14:00:40.897 回答
2

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

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

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

于 2016-07-20T07:01:18.133 回答
0
memset(array, 0, sizeof(int [n][n]));
于 2010-03-25T14:32:30.897 回答
0

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

(如果您的数组类型与 int 不同,则更改 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;
}

于 2010-03-25T16:53:14.400 回答
0

你可以试试这个

int array[20,30] = {{0}};
于 2016-04-19T19:27:18.813 回答
-2

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

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

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

size_t arrayByteSize = sizeof(int) * i * j; 
int *array = malloc(array2dByteSite);
bzero(array, arrayByteSize);
于 2010-03-25T14:06:36.337 回答