Pregunta

Alguien aquí recientemente me señaló en un código mío que estoy usando

char* name = malloc(256*sizeof(char));
// more code
free(name);

Tenía la impresión de que esta forma de configurar una matriz era idéntica a la del uso

char name[256];

y que ambas formas requerirían el uso de free (). ¿Me equivoco? Y si es así, ¿podría alguien explicar en términos bajos cuál es la diferencia?

¿Fue útil?

Solución

En el primer código, la memoria se asigna dinámicamente en el montón. Esa memoria necesita ser liberada con free (). Su vida útil es arbitraria: puede cruzar límites de funciones, etc.

En el segundo código, los 256 bytes se asignan en la pila y se reclaman automáticamente cuando la función regresa (o al final del programa si está fuera de todas las funciones). Por lo tanto, no tienes que (y no puedes) llamar gratis () a él. No puede filtrarse, pero tampoco vivirá más allá del final de la función.

Elija entre los dos según los requisitos de la memoria.

Anexo (Pax):

Si puedo agregar algo a esto, Ned, la mayoría de las implementaciones normalmente proporcionarán más almacenamiento que pila (al menos de forma predeterminada). Esto normalmente no importa para 256 bytes a menos que ya se esté quedando sin pila o haciendo cosas muy recursivas.

Además, sizeof (char) siempre es 1 de acuerdo con el estándar, por lo que no necesita esa multiplicación superflua. A pesar de que el compilador probablemente lo optimizará, hace que el código sea feo IMNSHO.

Adenda final (Pax).

Otros consejos

  

y que ambas formas requerirían el uso de free ().

No, solo el primero necesita el uso de un libre. El segundo se asigna en la pila. Eso hace que sea increíblemente rápido para asignar. Mira aquí:

void doit() {
    /* ... */
    /* SP += 10 * sizeof(int) */
    int a[10];
    /* ... (using a) */

} /* SP -= 10 */

Cuando lo creas, el compilador en tiempo de compilación conoce su tamaño y asignará el tamaño correcto en la pila para él. La pila es una gran parte de la memoria continua ubicada en algún lugar. Poner algo en la pila solo incrementará (o disminuirá dependiendo de su plataforma) el apilador. Salir fuera del alcance hará lo contrario, y su matriz se liberará. Eso sucederá automáticamente. Por lo tanto, las variables creadas de esa manera tienen una duración de almacenamiento automática .

El uso de malloc es diferente. Ordenará una porción de memoria grande arbitraria (desde un lugar llamado freestore ). El tiempo de ejecución tendrá que buscar un bloque de memoria razonablemente grande. El tamaño puede determinarse en tiempo de ejecución, por lo que el compilador generalmente no puede optimizarlo en tiempo de compilación. Debido a que el puntero puede salirse del alcance o copiarse, no hay un acoplamiento inherente entre la memoria asignada y el puntero al que se asigna la dirección de la memoria, por lo que la memoria aún está asignada incluso si dejó la función hace mucho tiempo. . Tienes que llamar gratis para pasar la dirección que obtuviste de malloc manualmente si es el momento para hacerlo.

Algunos " recientes " La forma de C, llamada C99, le permite asignar a las matrices un tamaño de tiempo de ejecución. Es decir, está permitido hacer:

void doit(int n) {
    int a[n]; // allocate n * sizeof(int) size on the stack */
}

Pero esa característica debería evitarse si no tienes una razón para usarla. Una razón es que no es a prueba de fallos: si ya no hay memoria disponible, cualquier cosa puede pasar. Otra es que C99 no es muy portátil entre compiladores.

Aquí hay una tercera posibilidad, que es que la matriz se puede declarar externa a una función, pero estáticamente, por ejemplo,

// file foo.c
char name[256];

int foo() {
    // do something here.
}

Me sorprendí bastante en las respuestas a otra pregunta en el sentido de que alguien sintió que esto era inapropiado en C; aquí ni siquiera se menciona, y estoy un poco confundida y sorprendida (por ejemplo, "¿qué están enseñando a los niños en la escuela en estos días?").

Si usa esta definición, la memoria se asigna de forma estática, ni en el montón ni en la pila, sino en el espacio de datos en la imagen. Por lo tanto, no se debe administrar como malloc / free, ni tiene que preocuparse por la reutilización de la dirección como lo haría con una definición automática.

Es útil recordar todo el " declarado " vs " definido " cosa aquí Aquí hay un ejemplo

/* example.c */

char buf1[256] ;           /* declared extern, defined in data space */
static char buf2[256] ;    /* declared static, defined in data space */
char * buf3 ;              /* declared extern, defined one ptr in data space */
int example(int c) {       /* c declared here, defined on stack */
    char buf4[256] ;       /* declared here, defined on stack   */
    char * buf5 = malloc(256)]   /* pointer declared here, defined on stack */
                           /* and buf4 is address of 256 bytes alloc'd on heap */
    buf3 = malloc(256);    /* now buf3 contains address of 256 more bytes on heap */

    return 0;              /* stack unwound; buf4 and buf5 lost.      */
                           /* NOTICE buf4 memory on heap still allocated */
                           /* so this leaks 256 bytes of memory */
}

Ahora en un archivo completamente diferente

/* example2.c */

extern char buf1[];             /* gets the SAME chunk of memory as from example.c */
static char buf2[256];          /* DIFFERENT 256 char buffer than example.c */
extern char * buf3 ;            /* Same pointer as from example.c */
void undoes() {
     free(buf3);                /* this will work as long as example() called first */
     return ;
}

Esto es incorrecto, la declaración de la matriz no requiere un libre. Además, si está dentro de una función, se asigna en la pila (si la memoria sirve) y se libera automáticamente con los retornos de la función. ¡No le pases una referencia a la persona que llama!

Desglosa tu declaración

char* name = malloc(256*sizeof(char)); // one statement
char *name; // Step 1 declare pointer to character
name = malloc(256*sizeof(char)); // assign address to pointer of memory from heap
name[2]; // access 3rd item in array
*(name+2); // access 3rd item in array
name++; // move name to item 1

Traducción: nombre ahora es un puntero al carácter al que se asigna la dirección de alguna memoria en el montón

char name[256]; // declare an array on the stack
name++; // error name is a constant pointer
*(name+2); // access 3rd item in array
name[2]; // access 3rd item in array
char *p = name;
p[2]; // access 3rd item in array
*(p+2); // access 3rd item in array
p++; // move p to item 1
p[0]; // item 1 in array

Traducción: el nombre es un puntero constante a un carácter que apunta a algo de memoria en la pila

En C las matrices y los punteros son más o menos lo mismo. Las matrices son punteros constantes a la memoria. La principal diferencia es que cuando llamas a malloc, tomas tu memoria del montón y cualquier memoria tomada del montón debe liberarse del montón. Cuando declara la matriz con un tamaño, se le asigna memoria de la pila. No puedes liberar esta memoria porque la libertad se crea para liberar la memoria del montón. La memoria en la pila se liberará automáticamente cuando la unidad de programa actual regrese. En el segundo ejemplo, free (p) también sería un error. p es un puntero de la matriz de nombres en la pila. Así que al liberar p estás intentando liberar la memoria en la pila.

Esto no es diferente de:

int n = 10;
int *p = &n;

liberar p en este caso sería un error porque p apunta a n, que es una variable en la pila. Por lo tanto, p tiene una ubicación de memoria en la pila y no se puede liberar.

int *p = (int *) malloc(sizeof(int));
*p = 10;
free(p);

en este caso, el libre es correcto porque p apunta a una ubicación de memoria en el montón que fue asignado por malloc.

dependiendo de dónde esté ejecutando esto, el espacio de la pila podría ser una gran prima. Si, por ejemplo, estás escribiendo el código BREW para los teléfonos de Verizon / Alltel, generalmente estás restringido a pilas minúsculas, pero cada vez tienes más acceso al montón.

Además, como char [] se usa con más frecuencia para cadenas, no es una mala idea permitir que el método de construcción de cadenas asigne la memoria que necesita para la cadena en cuestión, en lugar de esperar que siempre y siempre 256 (o cualquier número que decretas) será suficiente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top