回答:
memset(array, 0, sizeof(array[0][0]) * m * n);
どこm
とn
2次元配列の幅と高さは、(あなたの例では、あなたがそう、正方形の2次元配列を持っていますm == n
)。
memset
たぶん、1行だけをゼロにしてクラッシュしたと言ったので、ではなく他の行でクラッシュしていると思います。
int d0=10, d1=20; int arr[d0][d1]
、そしてmemset(arr, 0, sizeof arr);
予想される(でコンパイルGCC 3.4.6、として働いていた-std=c99 -Wall
フラグ)。「自分のマシンで動作する」とは、しゃがむことを意味しますが、動作するmemset(arr, 0, sizeof arr);
はずでした。配列全体で使用されるバイト数を返すsizeof arr
必要があります(d0 * d1 * sizeof(int))。 sizeof array[0] * m * n
配列の正しいサイズが得られません。
int array[][10]
、その後、sizeof(array) == sizeof(int*)
以来、最初の次元の大きさが知られていません。OPは配列の取得方法を指定しませんでした。
array
が本当に配列の場合、次のようにして「ゼロ化」できます。
memset(array, 0, sizeof array);
ただし、知っておくべき2つのポイントがあります。
array
が実際に「2次元配列」である場合、つまり、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()
のサイズarr[0]
はのサイズですarr[0]
。これは、「配列[5]のint
」です。int *
「減衰」は最初のレベルでのみ発生するため、これはのサイズではありませんf()
。そのため、正しいサイズの配列へのポインタを受け取ると宣言する必要があります。
したがって、先に述べたように、元々行っていたことが機能するのは、上記の2つの条件が満たされた場合のみです。そうでない場合は、他の人が言ったことを行う必要があります。
memset(array, 0, m*n*sizeof array[0][0]);
最後に、投稿しmemset()
たfor
ループは厳密には同等ではありません。ポインターや浮動小数点値などの特定のタイプでは、「すべてのビット0」がゼロに等しくないコンパイラーが存在する可能性があります(かつて存在していました)。私はあなたがそれについて心配する必要があるとは思いません。
memset(array, 0, n*n*sizeof array[0][0]);
あなたm*n
がn*n
正しくないことを意味すると思いますか?
memset
バイト(文字)レベルで動作します。以来1
または2
基礎となる表現で同じバイトを持っていない、あなたがそれを行うことはできませんmemset
。
int
あなたのシステムに4バイトである」という読者が簡単に合計を計算することができるように、最小限の作業例の前にどこか。
まあ、それを行う最も速い方法は、それをまったく行わないことです。
奇妙に聞こえますが、ここにいくつかの擬似コードがあります:
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配列が、四分木(動的な1つのマインドではない)、またはデータの行のコレクションを使用して実装された場合は、ブールフラグの効果をローカライズできますが、さらに多くのフラグが必要になります。クアッドツリーではルートノードに空のフラグを設定し、行の配列では各行にフラグを設定します。
「なぜ大きな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
これは、配列をクリアする必要はありませんが、機能します。そして、それは配列をクリアするよりもはるかに速くなります。私が言ったように、最速の方法はそもそもそれをしないことです。
あなたが本当に、本当にスピードにこだわっている(そして移植性にそれほど夢中になっていない)場合、これを行うための絶対最速の方法は、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ビット整数をゼロに設定します。
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
手作業で行うには、次のコードに従うのが最も速いと思います。速度をmemset関数と比較できますが、遅くなることはありません。
(配列タイプがintと異なる場合は、ptrおよびptr1ポインターのタイプをintに変更します)
#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;
}
memset
char型よりも遅くなります。
memset(array, 0, sizeof(int [n][n]));
これは、sizeof(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);
sizeof
オペレータ、array
ポインタではない(これは、配列を宣言された場合)。例については、私の回答を参照してください。