variable sized memoria manejo length feature estatica ejemplos dinamico dinamica caracteristicas arreglo array are c arrays memory c99 variable-length-array

sized - ¿Cómo asigna el compilador la memoria sin conocer el tamaño en tiempo de compilación?



variable size array c (5)

Escribí un programa en C que acepta la entrada de enteros del usuario, que se usa como el tamaño de una matriz de enteros, y usando ese valor declara una matriz de un tamaño dado, y lo confirmo verificando el tamaño de la matriz.

Código:

#include <stdio.h> int main(int argc, char const *argv[]) { int n; scanf("%d",&n); int k[n]; printf("%ld",sizeof(k)); return 0; }

y sorprendentemente es correcto! El programa puede crear la matriz del tamaño requerido.
Pero toda la asignación de memoria estática se realiza en tiempo de compilación, y durante el tiempo de compilación no se conoce el valor de n , entonces, ¿cómo es que el compilador puede asignar memoria del tamaño requerido?

Si podemos asignar la memoria requerida así, ¿para qué sirve la asignación dinámica usando malloc() y calloc() ?


Cuando se dice que el compilador asigna memoria para las variables en el momento de la compilación , significa que la ubicación de esas variables se decide y se incrusta en el código ejecutable que genera el compilador, no que el compilador esté haciendo espacio para ellas mientras funciona . La asignación de memoria dinámica real es realizada por el programa generado cuando se ejecuta.


En C, los medios por los cuales un compilador admite VLA (matrices de longitud variable) depende del compilador: no tiene que usar malloc() y puede (y a menudo usa) lo que a veces se llama memoria "apilada". por ejemplo, usando funciones específicas del sistema como alloca() que no son parte del estándar C. Si usa stack, el tamaño máximo de una matriz es típicamente mucho más pequeño de lo que es posible usando malloc() , porque los sistemas operativos modernos permiten que los programas sean mucho más pequeños cuota de memoria de pila.


Esto no es una "asignación de memoria estática". Su matriz k es una matriz de longitud variable (VLA), lo que significa que la memoria para esta matriz se asigna en tiempo de ejecución. El tamaño estará determinado por el valor de tiempo de ejecución de n .

La especificación del lenguaje no dicta ningún mecanismo de asignación específico, pero en una implementación típica, su k generalmente terminará siendo un simple puntero int * con el bloque de memoria real asignado en la pila en el tiempo de ejecución.

Para un VLA sizeof operador también se evalúa en el tiempo de ejecución, razón por la cual obtiene el valor correcto de él en su experimento. Simplemente use %zu (no %ld ) para imprimir valores de tipo size_t .

El propósito principal de malloc (y otras funciones de asignación de memoria dinámica) es anular las reglas de duración basadas en el alcance, que se aplican a los objetos locales. Es decir, la memoria asignada con malloc permanece asignada "para siempre", o hasta que la desasigne explícitamente de forma free . La memoria asignada con malloc no se desasigna automáticamente al final del bloque.

VLA, como en su ejemplo, no proporciona esta funcionalidad "que anula el alcance". Su matriz k todavía obedece a las reglas de vida normales basadas en el alcance: su vida útil finaliza al final del bloque. Por esta razón, en el caso general, VLA no puede reemplazar malloc y otras funciones de asignación de memoria dinámica.

Pero en casos específicos cuando no es necesario "derrotar el alcance" y simplemente usar malloc para asignar una matriz de tamaño de tiempo de ejecución, VLA podría ser visto como un reemplazo para malloc . Solo tenga en cuenta, nuevamente, que los VLA generalmente se asignan en la pila y la asignación de grandes cantidades de memoria en la pila hasta el día de hoy sigue siendo una práctica de programación bastante cuestionable.


La memoria para esta construcción, que se llama "matriz de longitud variable", VLA, se asigna en la pila, de manera similar a alloca . Exactamente cómo sucede esto depende exactamente de qué compilador está usando, pero esencialmente es un caso de calcular el tamaño cuando se conoce, y luego restar [1] el tamaño total del puntero de la pila.

Necesitas malloc y amigos porque esta asignación "muere" cuando dejas la función. [Y no es válido en C ++ estándar]

[1] Para procesadores típicos que usan una pila que "crece hacia cero".


La memoria para matrices de longitud variable claramente no puede asignarse estáticamente. Sin embargo, se puede asignar en la pila. En general, esto implica el uso de un "puntero de cuadro" para realizar un seguimiento de la ubicación de las funciones del cuadro de pila ante cambios dinámicamente determinados en el puntero de pila.

Cuando intento compilar su programa, parece que lo que realmente sucede es que la matriz de longitud variable se optimizó. Así que modifiqué su código para obligar al compilador a asignar la matriz.

#include <stdio.h> int main(int argc, char const *argv[]) { int n; scanf("%d",&n); int k[n]; printf("%s %ld",k,sizeof(k)); return 0; }

Godbolt compila para arm usando gcc 6.3 (usando arm porque puedo leer el ASM de arm) compila esto en https://godbolt.org/g/5ZnHfa . (comentarios míos)

main: push {fp, lr} ; Save fp and lr on the stack add fp, sp, #4 ; Create a "frame pointer" so we know where ; our stack frame is even after applying a ; dynamic offset to the stack pointer. sub sp, sp, #8 ; allocate 8 bytes on the stack (8 rather ; than 4 due to ABI alignment ; requirements) sub r1, fp, #8 ; load r1 with a pointer to n ldr r0, .L3 ; load pointer to format string for scanf ; into r0 bl scanf ; call scanf (arguments in r0 and r1) ldr r2, [fp, #-8] ; load r2 with value of n ldr r0, .L3+4 ; load pointer to format string for printf ; into r0 lsl r2, r2, #2 ; multiply n by 4 add r3, r2, #10 ; add 10 to n*4 (not sure why it used 10, ; 7 would seem sufficient) bic r3, r3, #7 ; and clear the low bits so it is a ; multiple of 8 (stack alignment again) sub sp, sp, r3 ; actually allocate the dynamic array on ; the stack mov r1, sp ; store a pointer to the dynamic size array ; in r1 bl printf ; call printf (arguments in r0, r1 and r2) mov r0, #0 ; set r0 to 0 sub sp, fp, #4 ; use the frame pointer to restore the ; stack pointer pop {fp, lr} ; restore fp and lr bx lr ; return to the caller (return value in r0) .L3: .word .LC0 .word .LC1 .LC0: .ascii "%d/000" .LC1: .ascii "%s %ld/000"