Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

422
Vistas
¿Cómo funciona alloca() a nivel de memoria?

Estoy tratando de descubrir cómo alloca() realmente funciona en un nivel de memoria. Desde la página del manual de Linux :

La función alloca() asigna bytes de tamaño de espacio en el marco de la pila de la persona que llama. Este espacio temporal se libera automáticamente cuando la función que llamó alloca() regresa a su llamador.

¿Significa esto que alloca() reenviará el puntero de la pila en n bytes? ¿O dónde exactamente se asigna la memoria recién creada?

¿Y no es esto exactamente lo mismo que las matrices de longitud variable ?

Sé que los detalles de implementación probablemente se dejen en manos del sistema operativo y demás. Pero quiero saber cómo se logra esto en general .

over 4 years ago · Santiago Trujillo
5 Respuestas
Responde la pregunta

0

Sí, alloca es funcionalmente equivalente a una matriz de longitud variable local, es decir, esto:

 int arr[n];

y esto:

 int *arr = alloca(n * sizeof(int));

ambos asignan espacio para n elementos de tipo int en la pila. Las únicas diferencias entre arr en cada caso es que 1) uno es un arreglo real y el otro es un puntero al primer elemento de un arreglo, y 2) el tiempo de vida del arreglo termina con su alcance adjunto, mientras que el tiempo de vida de la memoria alloca termina cuando la función devuelve. En ambos casos, la matriz reside en la pila.

Como ejemplo, dado el siguiente código:

 #include <stdio.h> #include <alloca.h> void foo(int n) { int a[n]; int *b=alloca(n*sizeof(int)); int c[n]; printf("&a=%p, b=%p, &c=%p\n", (void *)a, (void *)b, (void *)c); } int main() { foo(5); return 0; }

Cuando ejecuto esto me sale:

 &a=0x7ffc03af4370, b=0x7ffc03af4340, &c=0x7ffc03af4320

Lo que muestra que la memoria devuelta de alloca se encuentra entre la memoria de los dos VLA.

Los VLA aparecieron por primera vez en el estándar C en C99, pero alloca existía mucho antes. La página de manual de Linux dice:

DE ACUERDO A

Esta función no está en POSIX.1-2001.

Hay evidencia de que la función alloca() apareció en 32V, PWB, PWB.2, 3BSD y 4BSD. Hay una página de manual para ello en 4.3BSD. Linux usa la versión GNU.

BSD 3 se remonta a finales de los años 70, por lo que alloca fue un intento temprano no estandarizado de VLA antes de que se agregaran al estándar.

Hoy en día, a menos que esté usando un compilador que no admita VLA (como MSVC), realmente no hay razón para usar esta función, ya que los VLA ahora son una forma estandarizada de obtener la misma funcionalidad.

over 4 years ago · Santiago Trujillo Denunciar

0

La otra respuesta describe con precisión la mecánica de VLA y alloca() .

Sin embargo, existe una diferencia funcional significativa entre alloca() y VLA automático . La vida útil de los objetos.

En el caso de alloca() el tiempo de vida finaliza cuando la función regresa. Para los VLA, el objeto se libera cuando finaliza el bloque que lo contiene.

 char *a; int n = 10; { char A[n]; a = A; } // a is no longer valid { a = alloca(n); } // is still valid

Como resultado, es posible agotar fácilmente la pila en el ciclo mientras que no es posible hacerlo con VLA.

 for (...) { char *x = alloca(1000); // x is leaking with each iteration consuming stack }

contra

 for (...) { int n = 1000; char x[n]; // x is released }
over 4 years ago · Santiago Trujillo Denunciar

0

Aunque alloca parece una función desde el punto de vista de la sintaxis, no se puede implementar como una función normal en un entorno de programación moderno*. Debe considerarse como una característica del compilador con una interfaz similar a una función.

Tradicionalmente, los compiladores de C mantenían dos registros de puntero, un "puntero de pila" y un "puntero de marco" (o puntero base). El puntero de pila delimita la extensión actual de la pila. El puntero de marco guardó el valor del puntero de pila al ingresar a la función y se usa para acceder a las variables locales y para restaurar el puntero de pila al salir de la función.

Hoy en día, la mayoría de los compiladores no usan un puntero de marco por defecto en las funciones normales. Los formatos modernos de información de depuración/excepción lo han vuelto inútil, pero aún entienden lo que es y pueden usarlo cuando sea necesario.

En particular, para funciones con asignaciones o matrices de longitud variable, el uso de un puntero de marco permite que la función realice un seguimiento de la ubicación de su marco de pila mientras modifica dinámicamente el puntero de pila para acomodar la matriz de longitud variable.

Por ejemplo, construí el siguiente código en O1 para arm

 #include <alloca.h> int bar(void * baz); void foo(int a) { bar(alloca(a)); }

y obtuve (comentarios míos)

 foo(int): push {fp, lr} @ save existing link register and frame pointer add fp, sp, #4 @ establish frame pointer for this function add r0, r0, #7 @ add 7 to a ... bic r0, r0, #7 @ ... and clear the bottom 3 bits, thus rounding a up to the next multiple of 8 for stack alignment sub sp, sp, r0 @ allocate the space on the stack mov r0, sp @ make r0 point to the newly allocated space bl bar @ call bar with the allocated space sub sp, fp, #4 @ restore stack pointer and frame pointer pop {fp, pc} @ restore frame pointer to value at function entry and return.

Y sí, las matrices de asignación y longitud variable son muy similares (aunque, como señala otra respuesta, no son exactamente iguales). alloca parece ser la más antigua de las dos construcciones.


* Con un compilador lo suficientemente tonto/predecible es posible implementar alloca como una función en ensamblador. Específicamente, el compilador necesita hacerlo.

  • Cree constantemente un puntero de cuadro para todas las funciones.
  • Use constantemente el puntero de marco en lugar del puntero de pila para hacer referencia a las variables locales.
  • Utilice constantemente el puntero de pila en lugar del puntero de marco al configurar parámetros para llamadas a funciones.

Aparentemente, así es como se implementó por primera vez ( https://www.tuhs.org/cgi-bin/utree.pl?file=32V/usr/src/libc/sys/alloca.s ).

Supongo que es posible que uno también pueda tener la implementación real como una función de ensamblador, pero tener un caso especial en el compilador que lo hizo entrar en modo tonto/predecible cuando vio alloca, no sé si algún proveedor de compiladores hizo eso.

over 4 years ago · Santiago Trujillo Denunciar

0

La diferencia más importante entre alloca y VLA s es el caso de falla. El siguiente código:

 int f(int n) { int array[n]; return array == 0; } int g(int n) { int *array = alloca(n); return array == 0; }

El VLA no tiene posibilidad de detectar un fallo de asignación; que es algo muy poco C para imponer en una construcción de lenguaje. Alloca() está mucho mejor diseñado.

over 4 years ago · Santiago Trujillo Denunciar

0

alloca asigna memoria que se libera automáticamente cuando regresa la función que llamó alloca . Es decir, la memoria asignada con alloca es local para el "marco de pila" o contexto de una función en particular.

alloca no se puede escribir de forma portátil y es difícil de implementar en máquinas sin una pila convencional. Su uso es problemático (y la implementación obvia en una máquina basada en pila falla) cuando su valor devuelto se pasa directamente a otra función, como en

 fgets(alloca(100), 100, stdin)

Está buscando problemas si lo usa en cualquier lugar que no se ajuste a esta descripción. Es probable que tenga problemas si usa alloca() en cualquiera de estos lugares, porque podría haber algo en la pila en el punto en que se llama a alloca() :

  • Dentro de un bucle.
  • Dentro de cualquier bloque que comience con variables locales, excepto el bloque más externo de una función, especialmente si la memoria asignada se usa después de salir de este bloque.
  • Usar cualquier expresión más complicada que una variable de puntero en el lado izquierdo de una tarea, incluido un elemento de una matriz de punteros.
  • Donde el valor de retorno de alloca() se usa como argumento de función.
  • En cualquier contexto donde se use el valor del operador =, como

if ((pointer_variable = alloca(sizeof(struct something))) == NULL) { .... }

Y espero que alguien me pida que incluso ESA limitación altamente restrictiva no sea lo suficientemente conservadora para el código generado por algunos compiladores. Ahora, si se hace como un compilador incorporado, es posible que pueda solucionar los problemas.

Una vez que finalmente descubrí la función alloca() , funcionó razonablemente bien; según recuerdo, el uso principal fue en un Bison parser . Esos 128 bytes desperdiciados por invocación combinados con un tamaño de pila fijo podrían ser una molestia. ¿Por qué no GCC ? Porque este fue un intento de portar GCC , inicialmente usando compiladores cruzados, a una máquina que resultó tener apenas suficiente memoria para compilar GCC (1.35 más o menos) de forma nativa. Cuando salió GCC 2 , resultó ser una memoria suficiente para que la compilación nativa fuera imposible.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda