2010-03-25 17 views
77

quiero a cero en repetidas ocasiones una gran variedad 2d en C. Esto es lo que hago en la actualidad¿La forma más rápida de poner a cero una matriz de 2d en 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; 
    } 
} 

He intentado usar memset:

memset(array, 0, sizeof(array)) 

Pero esto sólo funciona para 1D matrices. Cuando imprimo el contenido de la matriz 2D, la primera fila es cero, pero luego recibí una gran cantidad de números aleatorios y se bloquea.

Respuesta

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

Dónde m y n son la anchura y la altura de la matriz de dos dimensiones (en su ejemplo, tiene un cuadrado matriz de dos dimensiones, por lo m == n).

+2

i y j debe ser n – Skizz

+0

@Skizz: Gracias! –

+0

Parece que no funciona. Me aparece el 'proceso devuelto -1073741819' en bloques de código, que es un fallo seg ¿no? – Eddy

2

¿Cómo se declaró su matriz 2D?

Si algo como:

int arr[20][30]; 

Puede cero, haciendo:

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

He usado una matriz char [10] [10]. Pero obtuve el error: muy pocos argumentos para funcionar 'memset', y 'memset (a, 0, sizeof (char) * 10 * 10);' funciona bien para mí. , ¿Cómo sucede? – noufal

-2

Esto sucede porque sizeof (matriz) le da el tamaño de asignación del objeto apuntado por gama. (array es solo un puntero a la primera fila de su matriz multidimensional). Sin embargo, asignó j matrices de tamaño i. En consecuencia, es necesario multiplicar el tamaño de una fila, que se devuelve por sizeof (array) con el número de filas que asignará, por ejemplo:

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

en cuenta también que sizeof (array) sólo funcionará para estáticamente asignada matrices. Para una matriz asignada dinámicamente escribiría

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

mejor uso calloc –

+0

La primera parte es incorrecta. Para el operador 'sizeof',' array' no es un puntero (si se declaró una matriz). Ver mi respuesta para un ejemplo. –

9

Bueno, la forma más rápida de hacerlo es no hacerlo en absoluto.

suena raro lo sé, aquí algo de pseudocódigo:

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

En realidad, sigue siendo la limpieza de la matriz, pero sólo cuando algo se está escribiendo en la matriz. Esto no es una gran ventaja aquí. Sin embargo, si la matriz 2D se implementó usando, por ejemplo, un árbol cuádruple (no una mente dinámica), o una colección de filas de datos, entonces puede localizar el efecto de la bandera booleana, pero necesitaría más banderas. En el árbol cuádruple, simplemente configure el indicador vacío para el nodo raíz, en el conjunto de filas solo establezca el indicador para cada fila.

Lo que lleva a la pregunta "¿por qué quieres poner en cero varias veces una gran matriz 2d"? ¿Para qué se usa la matriz? ¿Hay alguna manera de cambiar el código para que la matriz no necesite puesta a cero?

Por ejemplo, si usted tenía:

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

que es, lo utilizan para un buffer de acumulación, y luego cambiarlo como esto mejoraría el rendimiento no tiene fin:

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 

Esto no tiene' t requiere que la matriz se borre pero aún funciona. Y eso será mucho más rápido que borrar la matriz. Como dije, la manera más rápida es no hacerlo en primer lugar.

+0

Interesante manera alternativa de ver el problema. – Beska

+1

No estoy seguro si agregar una ramificación/comparación adicional para cada lectura vale la pena diferir la inicialización de la matriz en este caso (aunque puede ser suya). Si la matriz realmente es tan grande que el tiempo de inicialización representa una seria preocupación, entonces podría usar un hash en su lugar. – tixxit

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

array [n] [n] es el tamaño de 1 elemento de la matriz, por lo tanto, solo se inicializaría el primer elemento de la matriz. – EvilTeach

+0

Vaya. Estás en lo correcto. Quise poner una firma de tipo en los parens, no una búsqueda de matriz. Arreglado. – swestrup

66

Si array es realmente una matriz, entonces se puede "VACIARLA" con:

memset(array, 0, sizeof array); 

Pero hay dos puntos que debe saber:

  • Esto funciona sólo si array es realmente una "matriz de dos d", es decir, fue declarada T array[M][N]; para algún tipo T.
  • funciona solo en el alcance donde se declaró array. Si lo pasa a una función, el nombre arraydecays to a pointer y sizeof no le dará el tamaño de la matriz.

Vamos a hacer un experimento:

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

En mi máquina, las impresiones anteriores:

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 

Aunque arr es una matriz, que se descompone a un puntero a su primer elemento cuando pasó a f(), y por lo tanto los tamaños impresos en f() son "incorrectos". Además, en f() el tamaño de arr[0] es el tamaño de la matriz arr[0], que es una "matriz [5] de int". No tiene el tamaño de int *, porque el "decaimiento" solo ocurre en el primer nivel, y es por eso que tenemos que declarar f() como tomar un puntero a una matriz del tamaño correcto.

Por lo tanto, como dije, lo que estaba haciendo originalmente solo funcionará si se cumplen las dos condiciones anteriores. Si no es así, tendrá que hacer lo que otros han dicho:

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

Por último, memset() y el bucle for informados no son equivalentes en el sentido estricto. Podría haber (y ha habido) compiladores donde "todos los bits cero" no es igual a cero para ciertos tipos, como los punteros y los valores de coma flotante. Sin embargo, dudo que tengas que preocuparte por eso.

+1

+1 porque sizeof array es suficiente si realmente es 2-d. –

+0

'memset (array, 0, n * n * sizeof array [0] [0]);' Supongo que quiere decir 'm * n' no' n * n' ¿verdad? – Tagc

+0

+ Tagc sí, gracias por la corrección! –

5

Si inicializa la matriz con malloc, use calloc en su lugar; pondrá a cero tu matriz de forma gratuita. (La misma ejecución, obviamente, como memset, solo menos código para usted)

+0

¿Es esto más rápido que memset si quiero poner en cero repetidamente mi matriz? – Eddy

+0

calloc lo pondrá a cero una vez, en el momento de la inicialización, y probablemente no sea más rápido que llamar a malloc seguido de memset. Estás solo después de eso, y puedes usar memset si quieres volver a ponerlo en cero. A menos que su matriz sea realmente enorme, perf no es realmente una consideración aquí en cualquier máquina razonable. –

0

Creo que la forma más rápida de hacerlo a mano es siguiendo el código. Puede comparar su velocidad con la función memset, pero no debería ser más lenta.

(tipo de cambio de PTR y ptr1 punteros si su tipo de matriz es diferente a continuación, 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; 
} 

+0

Es probable que su código sea más lento que 'memset' para los tipos de caracteres. – tofro

7

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

__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.

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.

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_storeu que no pasa por la memoria caché (es decir, poner a cero la matriz no va a contaminar la caché) lo que podría dar alguna beneficios secundarios de rendimiento en algunas circunstancias.

ve aquí como referencia SSE2: http://msdn.microsoft.com/en-us/library/kcwz153a(v=vs.80).aspx

3

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

... al menos en GCC 4.8.

0

Puede probar este

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

Por favor, agregue más detalles –

1

Uso calloc en lugar de malloc. calloc iniciará todos los campos a 0.

int * a = (int *) calloc (n, tamaño de (int));

// todas las células de un han sido inicializado a 0

Cuestiones relacionadas