100

Cで大きな2次元配列を繰り返しゼロにしたい.これが私が現在行っていることです:

// 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 配列の内容を出力すると、最初の行はゼロになりますが、ランダムな大きな数値がロードされてクラッシュします。

4

12 に答える 12

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

mとは、2次元n配列の幅と高さです (この例では、正方形の 2 次元配列があるため、m == n)。

于 2010-03-25T13:59:44.460 に答える
85

が本当に配列である場合arrayは、次の方法で「ゼロにする」ことができます。

memset(array, 0, sizeof array);

ただし、知っておくべき 2 つのポイントがあります。

  • これは、arrayが実際に「2 次元配列」である場合、つまりT array[M][N];何らかの typeに対して宣言されている場合にのみ機能し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()のサイズarr[0]は配列のサイズでありarr[0]、「の配列 [5] int」です。int *「減衰」は最初のレベルでのみ発生するため、これは のサイズではありません。そのf()ため、正しいサイズの配列へのポインターを取得すると宣言する必要があります。

ですから、先ほど言ったように、あなたが本来行っていたことが機能するのは、上記の 2 つの条件が満たされた場合に限られます。そうでない場合は、他の人が言ったことを行う必要があります。

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

実際には、まだ配列をクリアしていますが、それは配列に何かが書き込まれているときだけです。これは大きな利点ではありません。ただし、たとえばクワッド ツリー (動的な 1 つの心ではない)、またはデータの行のコレクションを使用して 2D 配列が実装されている場合、ブール フラグの効果をローカライズできますが、より多くのフラグが必要になります。クワッド ツリーではルート ノードに空のフラグを設定するだけで、行の配列では各行にフラグを設定するだけです。

「なぜ大きな2次元配列を繰り返しゼロにしたいのですか」という質問につながるのはどれですか? 配列は何に使用されますか? 配列をゼロにする必要がないようにコードを変更する方法はありますか?

たとえば、次の場合:

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.

各ストア命令は、1回のヒットで4つの32ビットintをゼロに設定します。

pは16バイトにアラインされている必要がありますが、この制限はキャッシュに役立つため、速度にも適しています。もう1つの制限は、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 に答える
2

2D 配列はどのように宣言されましたか?

次のような場合:

int arr[20][30];

次のようにしてゼロにすることができます。

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

malloc の代わりに calloc を使用します。calloc はすべてのフィールドを 0 に初期化します。

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

// a のすべてのセルが 0 に初期化されました

于 2016-07-20T07:01:18.133 に答える
0

これを試すことができます

int array[20,30] = {{0}};
于 2016-04-19T19:27:18.813 に答える
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
memset(array, 0, sizeof(int [n][n]));
于 2010-03-25T14:32:30.897 に答える
-2

これは、 sizeof(array) によって、arrayが指すオブジェクトの割り当てサイズが得られるために発生します。( arrayは、多次元配列の最初の行への単なるポインターです)。ただし、サイズiのj個の配列を割り当てました。したがって、 sizeof(array) によって返される 1 行のサイズに、割り当てた行数を掛ける必要があります。たとえば、次のようになります。

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 に答える