2009-12-28 15 views
59

tengo el C siguiente código:El uso de malloc para la asignación de matrices multidimensionales con diferentes longitudes fila

int *a; 
size_t size = 2000*sizeof(int); 
a = (int *) malloc(size); 

que funciona muy bien. Pero si tengo el siguiente:

char **b = malloc(2000*sizeof *b); 

donde cada elemento de b tiene una longitud diferente.

¿Cómo es posible hacer lo mismo para b como lo hice para a; es decir, el siguiente código sería válido?

char *c; 
size_t size = 2000*sizeof(char *); 
c = (char *) malloc(size); 

Respuesta

67

En primer lugar, es necesario asignar matriz de punteros como char **c = malloc(N * sizeof(char*)), a continuación, asignar a cada fila con un separada llamar a malloc, probablemente en el bucle:


/* N is the number of rows */ 
/* note: c is char** */ 
if ((c = malloc(N*sizeof(char*))) == NULL) 
{ /* error */ } 

for (i = 0; i < N; i++) 
{ 
    /* x_i here is the size of given row, no need to 
    * multiply by sizeof(char), it's always 1 
    */ 
    if ((c[i] = malloc(x_i)) == NULL) 
    { /* error */ } 

    /* probably init the row here */ 
} 

/* access matrix elements: c[i] give you a pointer 
* to the row array, c[i][j] indexes an element 
*/ 
c[i][j] = 'a'; 

Si conoce el número total de elementos (por ejemplo N*M) se puede hacer esto en una sola asignación.

+2

Si asigna N * M bytes en una sola operación, entonces llena todos los c [i] s manualmente: c [i] = p + M * i; –

+2

Eso depende del tipo de c - si es char **, entonces sí, si es char *, entonces la indexación cambia: elemento [i] [j] ~ c [i * M + j]. –

+1

@Nikolai N Fetissov, hay muchos mallocs en el código, ¿cómo puede esto liberarse? usando también bucles for? – e19293001

3

Si todos los elementos de b tiene diferentes longitudes, entonces usted necesita para hacer algo como:

int totalLength = 0; 
for_every_element_in_b { 
    totalLength += length_of_this_b_in_bytes; 
} 
return (char **)malloc(totalLength); 
+1

no asigna memoria para una matriz de 1-dimensional de punteros char *. –

2

Creo que un enfoque de 2 pasos es mejor, porque las matrices c 2-d son justas y la matriz de matrices. El primer paso es asignar una única matriz, luego recorrerla asignando matrices para cada columna sobre la marcha. This article da buenos detalles.

46

La forma típica para asignar dinámicamente una matriz NxM de tipo T es

T **a = malloc(sizeof *a * N); 
if (a) 
{ 
    for (i = 0; i < N; i++) 
    { 
    a[i] = malloc(sizeof *a[i] * M); 
    } 
} 

Si cada elemento de la matriz tiene una longitud diferente, entonces reemplazar M con la longitud apropiada para ese elemento; por ejemplo

T **a = malloc(sizeof *a * N); 
if (a) 
{ 
    for (i = 0; i < N; i++) 
    { 
    a[i] = malloc(sizeof *a[i] * length_for_this_element); 
    } 
} 
+0

Si tengo el número total de int que voy a tener, pero no cuántos de ellos entran en cada matriz, ¿cómo debo proceder? – dietbacon

+0

¡Respuesta muy clara, gracias! ¿Podría también agregar una descripción de en qué orden "liberar" correctamente la memoria asignada? – Kagaratsch

+2

@Kagaratsch: en general, es gratis en el orden inverso que asignó, es decir, libere cada 'a [i]' primero, luego libere 'a'. –

10

El otro enfoque sería asignar un trozo contiguo de memoria que comprende bloque de cabecera para los punteros a las filas, así como bloque de cuerpo para almacenar datos reales en filas. A continuación, simplemente marque la memoria asignando direcciones de memoria en el cuerpo a los punteros en el encabezado por fila. Se vería como sigue:

int** 2dAlloc(int rows, int* columns) {  
    int header = rows * sizeof(int*); 

    int body = 0; 
    for(int i=0; i<rows; body+=columnSizes[i++]) { 
    } 
    body*=sizeof(int); 

    int** rowptr = (int**)malloc(header + body); 

    int* buf = (int*)(rowptr + rows); 
    rowptr[0] = buf; 
    int k; 
    for(k = 1; k < rows; ++k) { 
     rowptr[k] = rowptr[k-1] + columns[k-1]; 
    } 
    return rowptr; 
} 

int main() { 
    // specifying column amount on per-row basis 
    int columns[] = {1,2,3}; 
    int rows = sizeof(columns)/sizeof(int); 
    int** matrix = 2dAlloc(rows, &columns); 

    // using allocated array 
    for(int i = 0; i<rows; ++i) { 
     for(int j = 0; j<columns[i]; ++j) { 
      cout<<matrix[i][j]<<", "; 
     } 
      cout<<endl; 
    } 

    // now it is time to get rid of allocated 
    // memory in only one call to "free" 
    free matrix; 
} 

La ventaja de este enfoque es elegante liberación de la memoria y la capacidad de utilizar la notación de matriz similar a acceder a los elementos de la matriz 2D resultante.

+2

Algo a tener en cuenta: esta solución generalmente tendrá un mejor rendimiento con respecto a la coherencia del caché, ya que se garantiza que las filas individuales serán contiguas, a diferencia de los otros enfoques que asignan una fila a la vez, y puede conducir a una matriz cuyas partes componentes son dispersos en un montón altamente fragmentado. –

+2

Desafortunadamente, esto también tiene el efecto secundario de no garantizar la alineación para tipos que no sean del tamaño de un puntero. Ej: un sistema con punteros de 32 bits y dobles de 64 bits con un número impar de filas iniciará la primera columna de la fila de dobles en un límite no alineado para 'doble'. Es * muy * importante que se tenga en cuenta, ya que puede conducir fácilmente a un error de bus debido a una alineación incorrecta de datos. Una solución general debería garantizar que las filas de datos comiencen en un límite de 8 bytes, creando un espacio de asignación adicional y ajustándose en consecuencia al asignar punteros de fila al segmento del puntero principal. – WhozCraig

+0

@DmitryAleks: ¿Dónde declaras 'columnSizes [] '? – user2284570

27

La asignación de memoria equivalente para char a[10][20] sería la siguiente.

char **a; 

a=(char **) malloc(10*sizeof(char *)); 

for(i=0;i<10;i++) 
    a[i]=(char *) malloc(20*sizeof(char)); 

Espero que esto parezca simple de entender.

0

malloc no se asigna en límites específicos, por lo que se debe suponer que se asigna en un límite de bytes.

El puntero devuelto no se puede utilizar si se convierte a otro tipo, ya que el acceso a ese puntero probablemente producirá una violación de acceso a la memoria por parte de la CPU y la aplicación se cerrará inmediatamente.

0

2-D de matriz de asignación de memoria dinámica

int **a,i; 

// for any number of rows & columns this will work 
a = (int **)malloc(rows*sizeof(int *)); 
for(i=0;i<rows;i++) 
    *(a+i) = (int *)malloc(cols*sizeof(int)); 
Cuestiones relacionadas