2010-12-04 26 views
26

sé que podría hacerse utilizando malloc, pero no saben cómo usarlo todavía.¿Cómo declaro una matriz de tamaño inicial indefinido o no?

Por ejemplo, yo quería que el usuario introducir varios números utilizando un bucle infinito con un centinela para poner fin a ella (es decir, -1), pero ya que no sé todavía de entrada cuántos él/ella, yo Tengo que declarar una matriz sin tamaño inicial, pero también sé que no funcionará como este int arr []; en tiempo de compilación ya que tiene que tener un número definido de elementos.

Declarar con un tamaño exagerado como int arr [1000]; iba a funcionar, pero se siente tonto (y los residuos de memoria ya que sería asignar ese número entero 1000 bytes en la memoria) y me gustaría saber un más elegante manera de hacer esto.

+0

¿Qué tal 'malloc'? Las matrices no son punteros, y los punteros no son matrices. Pero se puede acceder cómodamente a los punteros como matrices y una matriz sin guiones se evalúa como un puntero al primer elemento. –

+2

@pst: ¿Has leído la pregunta? "Sé que podría hacerse utilizando malloc, pero aún no sé cómo usarlo". –

+0

indiqué señor que soy consciente de ello, pero no sé cómo usarlo, sin embargo, que explica cómo sería la mejor respuesta para mí: D – arscariosus

Respuesta

25

Esto se puede hacer mediante el uso de un puntero, y la asignación de memoria en el montón usando malloc. Tenga en cuenta que no hay forma de preguntar más tarde qué tan grande es ese bloque de memoria. Debe hacer un seguimiento del tamaño de la matriz usted mismo.

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

int main(int argc, char** argv) 
{ 
    /* declare a pointer do an integer */ 
    int *data; 
    /* we also have to keep track of how big our array is - I use 50 as an example*/ 
    const int datacount = 50; 
    data = malloc(sizeof(int) * datacount); /* allocate memory for 50 int's */ 
    if (!data) { /* If data == 0 after the call to malloc, allocation failed for some reason */ 
    perror("Error allocating memory"); 
    abort(); 
    } 
    /* at this point, we know that data points to a valid block of memory. 
    Remember, however, that this memory is not initialized in any way -- it contains garbage. 
    Let's start by clearing it. */ 
    memset(data, 0, sizeof(int)*datacount); 
    /* now our array contains all zeroes. */ 
    data[0] = 1; 
    data[2] = 15; 
    data[49] = 66; /* the last element in our array, since we start counting from 0 */ 
    /* Loop through the array, printing out the values (mostly zeroes, but even so) */ 
    for(int i = 0; i < datacount; ++i) { 
    printf("Element %d: %d\n", i, data[i]); 
    } 
} 

Eso es todo. Lo que sigue es una explicación más complicada de por qué funciona esto :)

No sé qué tan bien conoce los punteros C, pero el acceso a la matriz en C (como array[2]) es en realidad una abreviatura para acceder a la memoria mediante un puntero. Para acceder a la memoria apuntada por data, escriba *data. Esto se conoce como desreferenciación del puntero. Desde data es de tipo int *, entonces *data es de tipo int. Ahora a una información importante: (data + 2) significa "agregar el tamaño de bytes de 2 entradas a la dirección apuntada por data".

una matriz en C es sólo una secuencia de valores en la memoria adyacente. array[1] está justo al lado de array[0]. Entonces, cuando asignamos un gran bloque de memoria y queremos usarlo como una matriz, necesitamos una manera fácil de obtener la dirección directa de cada elemento dentro. Afortunadamente, C nos permite usar la notación de matriz en los punteros también. data[0] significa lo mismo que *(data+0), a saber, "acceder a la memoria apuntada por data". data[2] significa *(data+2), y accede al tercero int en el bloque de memoria.

+1

Gracias. Esta es la explicación completa que necesito, gracias por el esfuerzo extra señor, ahora puedo codificar para probar algunas cosas según su explicación. La última pregunta, ¿cómo aumentaría el tamaño de la matriz, suponiendo que quisiera extenderla más allá de los 50 elementos? – arscariosus

+0

Para aumentar el tamaño de la matriz, debe usar 'realloc'. Esta _debemos copiar y mover todo el bloque de memoria, por lo que no es barato. http://linux.die.net/man/3/realloc – gnud

+0

Una buena explicación, daría dos votos a favor si pudiera. – marcusshep

0

Una manera en que puedo imaginar es utilizar una lista enlazada para implementar un escenario de este tipo, si es necesario todos los números introducidos antes de que el usuario introduce algo que indica la terminación del bucle. (publicación como la primera opción, porque nunca lo hizo para la entrada del usuario, simplemente parecía interesante. derrochador pero artístico)

Otra forma es hacer la entrada en el búfer. Asignar un búfer, llenarlo, reasignar, si el bucle continúa (no elegante, pero la más racional para el caso de uso dado).

no considero el descrito es elegante sin embargo. Probablemente, que cambiaría el caso de uso (la más racional).

2

tratar de implementar la estructura de datos dinámica tal como un linked list

+1

Agh. No puedo contar la cantidad de C "veteranos" con los que me he topado, que no parecen darse cuenta de que cualquier otro tipo de esquema de almacenamiento dinámico es incluso posible, y mucho menos podría ser óptimo. Parece que las personas nunca pasan de esto. Prefiero recomendar que comiencen a implementar algo más parecido a un vector, como sugiere Aix, el daño es mucho menor en ese caso si no avanzan. –

+0

Por supuesto, las estructuras de datos más eficientes están ahí, de hecho, lo primero que se me ocurrió fue utilizar STL Vector antes de notar que en realidad estaba preguntando por C. La lista vinculada es más un concepto que una estructura de datos real. –

10

La forma en que se hace a menudo es como sigue:

  • asignar una matriz de algún tamaño inicial (bastante pequeña);
  • leen en esta matriz, el seguimiento de cuántos elementos que ha leído;
  • una vez que la matriz está llena, reasignar ella, duplicando el tamaño y la preservación (es decir, de copia) el contenido;
  • repita hasta que esté hecho.

me parece que este patrón aparece con bastante frecuencia.

Lo interesante de este método es que permite insertar N elementos en una matriz vacía uno por uno en el tiempo O(N) amortizado sin saber N de antemano.

+0

Buen truco. Esto parece una respuesta simple y, por lo tanto, sensata. –

1

malloc() (y sus amigos) free() y realloc() es la manera de hacer esto en C.

5

Moderno C, también conocido como C99, tiene variable length arrays, VLA. Desafortunadamente, no todos los compiladores soportan esto, pero si lo hace, esto sería una alternativa.

+1

Desafortunadamente, esto no le permite cambiar el tamaño ya que excede la conjetura inicial. – wnoise

+0

Gracias, acabo de enterarme de esto y es útil.Todavía intentaré descubrir cómo hacer esto en C89 solo por portabilidad. Muchas gracias. – arscariosus

1

Aquí hay un ejemplo de programa que lee stdin en un búfer de memoria que crece a medida que sea necesario. Es lo suficientemente simple como para dar una idea de cómo manejar este tipo de cosas. Una cosa que probablemente se haría de manera diferente en un programa real es cómo debe crecer la matriz en cada asignación: la mantuve pequeña aquí para ayudar a simplificar las cosas si quería pasar por un depurador. Un programa real probablemente usaría un incremento de asignación mucho más grande (a menudo, el tamaño de la asignación se duplica, pero si va a hacer eso, probablemente debería "limitar" el incremento a un tamaño razonable; podría no tener sentido doblar el asignación cuando ingresas a los cientos de megabytes).

Además, he usado acceso indexado a la memoria intermedia aquí como un ejemplo, pero en un programa real que probablemente no haría eso.

#include <stdlib.h> 
#include <stdio.h> 


void fatal_error(void); 

int main(int argc, char** argv) 
{ 
    int buf_size = 0; 
    int buf_used = 0; 

    char* buf = NULL; 
    char* tmp = NULL;  

    char c; 
    int i = 0; 

    while ((c = getchar()) != EOF) { 
     if (buf_used == buf_size) { 
      //need more space in the array 

      buf_size += 20; 
      tmp = realloc(buf, buf_size); // get a new larger array 
      if (!tmp) fatal_error(); 

      buf = tmp; 
     } 

     buf[buf_used] = c; // pointer can be indexed like an array 
     ++buf_used; 
    } 

    puts("\n\n*** Dump of stdin ***\n"); 

    for (i = 0; i < buf_used; ++i) { 
     putchar(buf[i]); 
    } 

    free(buf); 

    return 0; 
} 

void fatal_error(void) 
{ 
    fputs("fatal error - out of memory\n", stderr); 
    exit(1); 
} 

En este ejemplo se combina con ejemplos en otras respuestas deben darle una idea de cómo este tipo de cosas se maneja en un nivel bajo.

Cuestiones relacionadas