¿Cómo implementa GCC las matrices de longitud variable?

¿Cómo implementa GCC las matrices de longitud variable (VLA)? ¿Son estos arreglos esencialmente punteros al almacenamiento asignado dinámicamente como los devueltos por alloca?

La otra alternativa en la que podría pensar es que dicha matriz se asigna como última variable en una función, de modo que el desplazamiento de las variables se conoce durante el tiempo de comstackción. Sin embargo, el desplazamiento de un segundo VLA tampoco se conocería durante el tiempo de comstackción.

Aquí está el código de asignación (x86 – el código x64 es similar) para la siguiente línea de ejemplo tomada de algunos documentos de GCC para compatibilidad con VLA :

 char str[strlen (s1) + strlen (s2) + 1]; 

donde el cálculo para strlen (s1) + strlen (s2) + 1 está en eax (GCC MinGW 4.8.1 – sin optimizaciones):

 mov edx, eax sub edx, 1 mov DWORD PTR [ebp-12], edx mov edx, 16 sub edx, 1 add eax, edx mov ecx, 16 mov edx, 0 div ecx imul eax, eax, 16 call ___chkstk_ms sub esp, eax lea eax, [esp+8] add eax, 0 mov DWORD PTR [ebp-16], eax 

Entonces parece ser esencialmente alloca() .

Bueno, estas son solo algunas puñaladas en la oscuridad, basadas en las restricciones alrededor de los VLA, pero de todos modos:

Los VLA no pueden ser:

  • externo
  • miembros de la estructura
  • estático
  • declarado con límites no especificados (excepto por el prototipo de función)

Todo esto apunta a que VLA se asigna en la stack , en lugar del montón. Así que sí, los VLA probablemente son los últimos fragmentos de memoria de stack asignados siempre que se asigna un nuevo bloque (bloque como en el scope del bloque , estos son bucles, funciones, twigs o lo que sea).
Es también por eso que los VLA aumentan el riesgo de desbordamiento de stack, en algunos casos de manera significativa (advertencia: ¡ni siquiera piense en usar VLA en combinación con llamadas a funciones recursivas, por ejemplo!).
Esta es también la razón por la cual el acceso fuera de límites es muy probable que cause problemas: una vez que termina el bloque, cualquier cosa que apunte a lo que era memoria VLA apunta a una memoria no válida.
Pero en el lado positivo : esta es también la razón por la que estas matrices son seguras para hilos (debido a que los hilos tienen su propia stack) y por qué son más rápidas en comparación con la memoria de montón.

El tamaño de un VLA no puede ser:

  • un valor extern
  • cero o negativo

la restricción externa es bastante evidente, como lo es la no-cero, no negativa … sin embargo: si la variable que especifica el tamaño de un VLA es un int firmado, por ejemplo, el comstackdor no producirá un error : la evaluación, y por lo tanto la asignación, de un VLA se realiza durante el tiempo de ejecución , no en tiempo de comstackción. Por lo tanto, el tamaño de un VLA no puede, y no necesita ser, un dato durante el tiempo de comstackción .
Como MichaelBurr señaló correctamente, los VLA son muy similares a alloca memory, con uno, en mi humilde opinión, distinción crucial: la memoria asignada por alloca es válida desde el punto de asignación y durante el rest de la función. Los VLA tienen un scope de bloque, por lo que la memoria se libera una vez que salgas del bloque en el que se usa un VLA:

 void alloca_diff( void ) { char *alloca_c, *vla_c; for (int i=1;i<10;++i) { char *alloca_mem = alloca(i*sizeof(*alloca_mem)); alloca_c = alloca_mem;//valid char vla_arr[i]; vla_c = vla_arr;//invalid }//end of scope, VLA memory is freed printf("alloca: %c\n", *alloca_c);//fine printf("vla: %c\n\", *vla_c);//undefined behaviour... avoid! }//end of function alloca memory is freed, irrespective of block scope