memory-management - cmemory heap ¿Qué y dónde están la pila y el montón?




12 Answers

Apilar:

  • Almacenado en la memoria RAM de la computadora al igual que el montón.
  • Las variables creadas en la pila quedarán fuera del alcance y se desasignarán automáticamente.
  • Mucho más rápido de asignar en comparación con las variables en el montón.
  • Implementado con una estructura de datos de pila real.
  • Almacena datos locales, direcciones de retorno, utilizadas para pasar parámetros.
  • Puede tener un desbordamiento de pila cuando se usa demasiado de la pila (en su mayoría de recursión infinita o demasiado profunda, asignaciones muy grandes).
  • Los datos creados en la pila se pueden utilizar sin punteros.
  • Usaría la pila si sabe exactamente cuántos datos necesita asignar antes del tiempo de compilación y no es demasiado grande.
  • Por lo general, el tamaño máximo ya está determinado cuando se inicia el programa.

Montón:

  • Almacenado en la memoria RAM de la computadora al igual que la pila.
  • En C ++, las variables en el montón deben destruirse manualmente y nunca quedar fuera de alcance. Los datos se liberan con delete , delete[] o free .
  • Más lento para asignar en comparación con las variables en la pila.
  • Se utiliza a pedido para asignar un bloque de datos para uso del programa.
  • Puede tener fragmentación cuando hay muchas asignaciones y desasignaciones.
  • En C ++ o C, los datos creados en el montón serán señalados por punteros y asignados con new o malloc respectivamente.
  • Puede tener errores de asignación si se solicita un búfer demasiado grande para ser asignado.
  • Usaría el montón si no sabe exactamente cuántos datos necesitará en el tiempo de ejecución o si necesita asignar una gran cantidad de datos.
  • Responsable de las fugas de memoria.

Ejemplo:

int foo()
{
  char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
  bool b = true; // Allocated on the stack.
  if(b)
  {
    //Create 500 bytes on the stack
    char buffer[500];

    //Create 500 bytes on the heap
    pBuffer = new char[500];

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;
stack vs heap

Los libros de lenguaje de programación explican que los tipos de valor se crean en la pila y los tipos de referencia se crean en el montón , sin explicar cuáles son estas dos cosas. No he leído una explicación clara de esto. Entiendo lo que es una pila . Pero,

  • ¿Dónde y qué son (físicamente en la memoria de una computadora real)?
  • ¿En qué medida están controlados por el sistema operativo o el tiempo de ejecución de idioma?
  • ¿Cuál es su alcance?
  • ¿Qué determina el tamaño de cada uno de ellos?
  • ¿Qué hace que uno sea más rápido?



(He movido esta respuesta de otra pregunta que fue más o menos una imitación de esta).

La respuesta a su pregunta es específica de la implementación y puede variar entre compiladores y arquitecturas de procesadores. Sin embargo, aquí hay una explicación simplificada.

  • Tanto la pila como el montón son áreas de memoria asignadas desde el sistema operativo subyacente (a menudo, la memoria virtual que se asigna a la memoria física a pedido).
  • En un entorno de subprocesos múltiples, cada subproceso tendrá su propia pila completamente independiente, pero compartirán el montón. El acceso simultáneo debe controlarse en el montón y no es posible en la pila.

El montón

  • El montón contiene una lista enlazada de bloques usados ​​y libres. Las nuevas asignaciones en el montón (por new o malloc ) se satisfacen creando un bloque adecuado a partir de uno de los bloques libres. Esto requiere actualizar la lista de bloques en el montón. Esta información meta sobre los bloques en el montón también se almacena en el montón a menudo en un área pequeña justo en frente de cada bloque.
  • A medida que crece el montón, a menudo se asignan nuevos bloques desde direcciones inferiores a direcciones superiores. Por lo tanto, puede pensar en el montón como un montón de bloques de memoria que crece en tamaño a medida que se asigna la memoria. Si el montón es demasiado pequeño para una asignación, el tamaño a menudo se puede aumentar al adquirir más memoria del sistema operativo subyacente.
  • La asignación y desasignación de muchos bloques pequeños puede dejar el montón en un estado en el que hay muchos pequeños bloques libres intercalados entre los bloques utilizados. Una solicitud para asignar un bloque grande puede fallar porque ninguno de los bloques libres es lo suficientemente grande como para satisfacer la solicitud de asignación, aunque el tamaño combinado de los bloques libres sea lo suficientemente grande. Esto se llama fragmentación del montón .
  • Cuando un bloque usado que es adyacente a un bloque libre se desasigna, el nuevo bloque libre se puede fusionar con el bloque libre adyacente para crear un bloque libre más grande que reduce efectivamente la fragmentación del montón.

La pila

  • La pila a menudo funciona en conjunto con un registro especial en la CPU llamada el puntero de pila . Inicialmente, el puntero de la pila apunta a la parte superior de la pila (la dirección más alta en la pila).
  • La CPU tiene instrucciones especiales para insertar valores en la pila y sacarlos de la pila. Cada inserción almacena el valor en la ubicación actual del puntero de pila y disminuye el puntero de pila. Una ventana emergente recupera el valor al que apunta el puntero de la pila y luego aumenta el puntero de la pila (no se confunda con el hecho de que agregar un valor a la pila disminuye el puntero de la pila y eliminar un valor aumenta . Recuerde que la pila aumenta hasta El fondo). Los valores almacenados y recuperados son los valores de los registros de la CPU.
  • Cuando se llama una función, la CPU utiliza instrucciones especiales que empujan el puntero de la instrucción actual, es decir, la dirección del código que se ejecuta en la pila. La CPU luego salta a la función estableciendo el puntero de instrucción a la dirección de la función llamada. Más tarde, cuando la función regresa, el puntero de instrucción anterior se extrae de la pila y la ejecución se reanuda en el código justo después de la llamada a la función.
  • Cuando se ingresa una función, el puntero de pila se reduce para asignar más espacio en la pila para las variables locales (automáticas). Si la función tiene una variable local de 32 bits, se reservan cuatro bytes en la pila. Cuando la función regresa, el puntero de la pila se mueve hacia atrás para liberar el área asignada.
  • Si una función tiene parámetros, estos se insertan en la pila antes de la llamada a la función. El código de la función puede navegar hacia arriba en la pila desde el puntero de pila actual para ubicar estos valores.
  • La función de anidar llama al trabajo como un encanto. Cada nueva llamada asignará los parámetros de la función, la dirección de retorno y el espacio para las variables locales y estos registros de activación se pueden apilar para llamadas anidadas y se desenrollarán de la manera correcta cuando las funciones regresen.
  • Como la pila es un bloque de memoria limitado, puede provocar un desbordamiento de pila llamando a demasiadas funciones anidadas y / o asignando demasiado espacio para las variables locales. A menudo, el área de memoria utilizada para la pila se configura de tal manera que escribir debajo de la parte inferior (la dirección más baja) de la pila provocará una captura o excepción en la CPU. El Runtime puede capturar esta condición excepcional y convertirla en algún tipo de excepción de desbordamiento de pila.

¿Se puede asignar una función en el montón en lugar de una pila?

No, los registros de activación para las funciones (es decir, las variables locales o automáticas) se asignan en la pila que se usa no solo para almacenar estas variables, sino también para realizar un seguimiento de las llamadas de función anidadas.

La forma en que se gestiona el montón depende realmente del entorno de ejecución. C usa malloc y C ++ usa new , pero muchos otros lenguajes tienen recolección de basura.

Sin embargo, la pila es una característica de más bajo nivel estrechamente vinculada a la arquitectura del procesador. Hacer crecer el montón cuando no hay suficiente espacio no es demasiado difícil, ya que se puede implementar en la llamada de la biblioteca que maneja el montón. Sin embargo, a menudo es imposible hacer crecer la pila, ya que el desbordamiento de pila solo se descubre cuando es demasiado tarde; y cerrar la cadena de ejecución es la única opción viable.




La pila Cuando llama a una función, los argumentos de esa función más algunos gastos generales se colocan en la pila. Algunos datos (como dónde ir en la devolución) también se almacenan allí. Cuando declara una variable dentro de su función, esa variable también se asigna en la pila.

La desasignación de la pila es bastante simple porque siempre se desasigna en el orden inverso en el que se asigna. Las cosas de la pila se agregan a medida que ingresa las funciones, los datos correspondientes se eliminan a medida que se salen. Esto significa que tiende a permanecer dentro de una pequeña región de la pila a menos que llame muchas funciones que llamen a muchas otras funciones (o cree una solución recursiva).

El montón El montón es un nombre genérico para colocar los datos que crea sobre la marcha. Si no sabe cuántas naves espaciales creará su programa, es probable que utilice el operador nuevo (o malloc o equivalente) para crear cada nave espacial. Esta asignación se mantendrá durante un tiempo, por lo que es probable que liberemos las cosas en un orden diferente al que las creamos.

Por lo tanto, el montón es mucho más complejo, ya que terminan siendo regiones de memoria que no se utilizan intercaladas con fragmentos que están: la memoria se fragmenta. Encontrar la memoria libre del tamaño que necesita es un problema difícil. Esta es la razón por la que el montón debe evitarse (aunque todavía se usa a menudo).

Implementación La implementación tanto de la pila como del montón generalmente depende del tiempo de ejecución / OS. A menudo, los juegos y otras aplicaciones que son críticas para el rendimiento crean sus propias soluciones de memoria que toman una gran cantidad de memoria del montón y luego la distribuyen internamente para evitar depender del sistema operativo para la memoria.

Esto solo es práctico si el uso de la memoria es bastante diferente de la norma, es decir, para juegos en los que se carga un nivel en una operación enorme y se puede eliminar todo el lote en otra operación enorme.

Ubicación física en la memoria Esto es menos relevante de lo que cree debido a una tecnología llamada memoria virtual que hace que su programa piense que tiene acceso a una dirección determinada donde los datos físicos se encuentran en otro lugar (¡incluso en el disco duro!). Las direcciones que obtiene para la pila están en orden creciente a medida que su árbol de llamadas se profundiza. Las direcciones para el montón son impredecibles (es decir, específicas de la implementación) y, francamente, no son importantes.




Otros han respondido bastante bien a los golpes amplios, así que les daré algunos detalles.

  1. La pila y el montón no necesitan ser singulares. Una situación común en la que tiene más de una pila es si tiene más de un hilo en un proceso. En este caso cada hilo tiene su propia pila. También puede tener más de un montón, por ejemplo, algunas configuraciones de DLL pueden dar como resultado que se asignen diferentes DLL de diferentes montones, por lo que generalmente es una mala idea liberar memoria asignada por una biblioteca diferente.

  2. En C puede obtener el beneficio de la asignación de longitud variable mediante el uso de alloca , que asigna en la pila, en lugar de asignar, que asigna en el montón. Esta memoria no sobrevivirá a su declaración de devolución, pero es útil para un buffer de memoria virtual.

  3. Hacer un gran buffer temporal en Windows que no usas mucho no es gratis. Esto se debe a que el compilador generará un bucle de prueba de pila que se invoca cada vez que se ingresa a su función para asegurarse de que existe la pila (porque Windows usa una única página de protección al final de la pila para detectar cuándo debe aumentar la pila) Si accede a la memoria más de una página del final de la pila, se bloqueará). Ejemplo:

void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}



La pila es una parte de la memoria que se puede manipular mediante varias instrucciones clave en lenguaje ensamblador, como 'pop' (eliminar y devolver un valor de la pila) y 'push' (empujar un valor a la pila), pero también llamar ( llamar a una subrutina: esto empuja la dirección para volver a la pila) y regresar (retorno de una subrutina: saca la dirección de la pila y salta a ella). Es la región de la memoria debajo del registro de puntero de pila, que se puede configurar según sea necesario. La pila también se usa para pasar argumentos a subrutinas, y también para conservar los valores en los registros antes de llamar a subrutinas.

El montón es una parte de la memoria que se entrega a una aplicación por el sistema operativo, normalmente a través de una syscall como malloc. En los sistemas operativos modernos, esta memoria es un conjunto de páginas a las que solo tiene acceso el proceso de llamada.

El tamaño de la pila se determina en el tiempo de ejecución, y generalmente no crece después de que se inicie el programa. En un programa en C, la pila debe ser lo suficientemente grande como para contener cada variable declarada dentro de cada función. El montón crecerá dinámicamente según sea necesario, pero el sistema operativo finalmente está haciendo la llamada (a menudo aumentará el montón más que el valor solicitado por malloc, de modo que al menos algunos mallocs futuros no tendrán que volver al núcleo para obtener más memoria. Este comportamiento es a menudo personalizable)

Debido a que ha asignado la pila antes de iniciar el programa, nunca necesita malloc antes de poder usar la pila, por lo que es una ligera ventaja allí. En la práctica, es muy difícil predecir qué será rápido y qué será lento en los sistemas operativos modernos que tienen subsistemas de memoria virtual, porque la forma en que se implementan las páginas y dónde se almacenan es un detalle de la implementación.




¿Qué es una pila?

Una pila es una pila de objetos, generalmente uno que está bien ordenado.

Las pilas en las arquitecturas de computación son regiones de memoria donde los datos se agregan o eliminan de la manera más reciente.
En una aplicación de subprocesos múltiples, cada subproceso tendrá su propia pila.

¿Qué es un montón?

Un montón es una colección desordenada de cosas apiladas al azar.

En las arquitecturas de computación, el montón es un área de memoria asignada dinámicamente que es administrada automáticamente por el sistema operativo o la biblioteca del administrador de memoria.
La memoria en el montón se asigna, se desasigna y se redimensiona regularmente durante la ejecución del programa, y ​​esto puede llevar a un problema llamado fragmentación.
La fragmentación se produce cuando los objetos de memoria se asignan con pequeños espacios intermedios que son demasiado pequeños para contener objetos de memoria adicionales.
El resultado neto es un porcentaje del espacio de almacenamiento dinámico que no se puede utilizar para otras asignaciones de memoria.

Ambos juntos

En una aplicación de subprocesos múltiples, cada subproceso tendrá su propia pila. Pero, todos los diferentes hilos compartirán el montón.
Debido a que los distintos subprocesos comparten el montón en una aplicación de subprocesos múltiples, esto también significa que debe haber cierta coordinación entre los subprocesos para que no intenten acceder y manipular las mismas piezas de memoria en el montón al mismo tiempo.

¿Cuál es más rápido - la pila o el montón? ¿Y por qué?

La pila es mucho más rápida que el montón.
Esto se debe a la forma en que se asigna la memoria en la pila.
La asignación de memoria en la pila es tan simple como mover el puntero de la pila hacia arriba.

Para personas nuevas en la programación, probablemente sea una buena idea usar la pila, ya que es más fácil.
Debido a que la pila es pequeña, desearía usarla cuando sepa exactamente cuánta memoria necesitará para sus datos, o si sabe que el tamaño de sus datos es muy pequeño.
Es mejor usar el montón cuando sabe que necesitará mucha memoria para sus datos, o simplemente no está seguro de cuánta memoria necesitará (como con una matriz dinámica).

Modelo de memoria de Java

La pila es el área de la memoria donde se almacenan las variables locales (incluidos los parámetros del método). Cuando se trata de variables de objeto, estas son meramente referencias (punteros) a los objetos reales en el montón.
Cada vez que se crea una instancia de un objeto, una parte de la memoria del montón se reserva para mantener los datos (estado) de ese objeto. Como los objetos pueden contener otros objetos, algunos de estos datos pueden contener referencias a esos objetos anidados.




Simplemente, la pila es donde se crean las variables locales. Además, cada vez que llama a una subrutina, el contador del programa (puntero a la siguiente instrucción de la máquina) y cualquier registro importante, ya veces los parámetros se insertan en la pila. Luego, cualquier variable local dentro de la subrutina se coloca en la pila (y se usa desde allí). Cuando la subrutina termina, todo eso vuelve a salir de la pila. La PC y los datos de registro se vuelven a colocar en el lugar en el que aparecían, para que su programa pueda continuar su camino alegre.

El montón es el área de memoria. Las asignaciones de memoria dinámica están hechas de (llamadas explícitas "nuevas" o "asignar"). Es una estructura de datos especial que puede realizar un seguimiento de bloques de memoria de diferentes tamaños y su estado de asignación.

En los sistemas "clásicos", la RAM se distribuyó de tal manera que el puntero de pila comenzó en la parte inferior de la memoria, el puntero del montón comenzó en la parte superior y crecieron uno hacia el otro. Si se superponen, estás fuera de la memoria RAM. Sin embargo, eso no funciona con los sistemas operativos modernos con múltiples subprocesos. Cada hilo debe tener su propia pila, y esos pueden ser creados dinámicamente.




Apilar

  • Acceso muy rapido
  • No es necesario desasignar explícitamente las variables
  • El espacio es administrado eficientemente por la CPU, la memoria no se fragmentará
  • Solo variables locales
  • Límite en el tamaño de la pila (depende del sistema operativo)
  • Las variables no pueden ser redimensionadas

Montón

  • Se puede acceder a las variables globalmente
  • No hay límite en el tamaño de la memoria
  • Acceso (relativamente) más lento
  • No se garantiza un uso eficiente del espacio, la memoria puede fragmentarse con el tiempo a medida que se asignan los bloques de memoria y luego se liberan
  • Debe gestionar la memoria (se encarga de asignar y liberar variables).
  • Las variables se pueden cambiar de tamaño usando realloc ()



  • Introducción

La memoria física es el rango de las direcciones físicas de las celdas de memoria en las que una aplicación o sistema almacena sus datos, código, etc. durante la ejecución. La administración de memoria denota la administración de estas direcciones físicas al intercambiar los datos de la memoria física a un dispositivo de almacenamiento y luego volver a la memoria física cuando sea necesario. El sistema operativo implementa los servicios de gestión de memoria utilizando memoria virtual. Como desarrollador de aplicaciones C #, no necesita escribir ningún servicio de administración de memoria. El CLR utiliza los servicios de administración de memoria del sistema operativo subyacente para proporcionar el modelo de memoria para C # o cualquier otro lenguaje de alto nivel dirigido al CLR.

La Figura 4-1 muestra la memoria física que ha sido abstraída y administrada por el sistema operativo, utilizando el concepto de memoria virtual. La memoria virtual es la vista abstracta de la memoria física, administrada por el sistema operativo. La memoria virtual es simplemente una serie de direcciones virtuales, y estas direcciones virtuales son traducidas por la CPU a la dirección física cuando es necesario.

Figura 4-1. Abstracción de la memoria CLR

El CLR proporciona la capa abstracta de administración de memoria para el entorno de ejecución virtual, utilizando los servicios de memoria operativa. Los conceptos abstraídos que usa CLR son AppDomain, thread, stack, heapmemorymagenmapped, y así sucesivamente. El concepto del dominio de aplicación (dominio de aplicación) le da a su aplicación un entorno de ejecución aislado.

  • Interacción de memoria entre el CLR y OS

Al observar el seguimiento de la pila mientras se depura la siguiente aplicación C #, usando WinDbg, verá cómo el CLR usa los servicios de administración de memoria del sistema operativo subyacente (por ejemplo, el método HeapFree de KERNEL32.dll, el método RtlpFreeHeap de ntdll.dll) para implementar su propio modelo de memoria:

using System;
namespace CH_04
{
    class Program
    {
        static void Main(string[] args)
        {
            Book book = new Book();
            Console.ReadLine();
        }
    }

    public class Book
    {
        public void Print() { Console.WriteLine(ToString()); }
    }
}

El ensamblado compilado del programa se carga en WinDbg para iniciar la depuración. Utiliza los siguientes comandos para inicializar la sesión de depuración:

0: 000> sxe ld clrjit

0: 000> g

0: 000> .loadby sos clr

0: 000> .load C: \ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ sos.dll

Luego, establece un punto de interrupción en el método Principal de la clase de Programa, usando el comando! Bpmd:

0: 000>! Bpmd CH_04.exe CH_04.Program.Main

Para continuar la ejecución y romper el punto de interrupción, ejecute el comando g:

0: 000> g

Cuando la ejecución se interrumpe en el punto de interrupción, utiliza el comando! Eestack para ver los detalles de seguimiento de la pila de todos los subprocesos que se ejecutan para el proceso actual. La siguiente salida muestra el seguimiento de la pila para todos los subprocesos que se ejecutan para la aplicación CH_04.exe:

0: 000>! Eestack

Hilo 0

Marco actual: (MethodDesc 00233800 +0 CH_04.Program.Main (System.String []))

ChildEBP RetAddr Caller, Callee

0022ed24 5faf21db clr! CallDescrWorker + 0x33

/ traza eliminada /

0022f218 77712d68 ntdll! RtlFreeHeap + 0x142, llamando a ntdll! RtlpFreeHeap

0022f238 771df1ac KERNEL32! HeapFree + 0x14, llamando a ntdll! RtlFreeHeap

0022f24c 5fb4c036 clr! EEHeapFree + 0x36, llamando a KERNEL32! HeapFree

0022f260 5fb4c09d clr! EEHeapFreeInProcessHeap + 0x24, llamando a clr! EEHeapFree

0022f274 5fb4c06d clr! Operator delete [] + 0x30, llamando a clr! EEHeapFreeInProcessHeap / trace remove /

0022f4d0 7771316f ntdll! RtlpFreeHeap + 0xb7a, llamando a ntdll! _SEH_epilog4

0022f4d4 77712d68 ntdll! RtlFreeHeap + 0x142, llamando a ntdll! RtlpFreeHeap

0022f4f4 771df1ac KERNEL32! HeapFree + 0x14, llamando a ntdll! RtlFreeHeap

/ traza eliminada /

Este seguimiento de pila indica que el CLR utiliza los servicios de administración de memoria del sistema operativo para implementar su propio modelo de memoria. Cualquier operación de memoria en .NET pasa a través de la capa de memoria CLR a la capa de administración de memoria del sistema operativo.

La Figura 4-2 ilustra un modelo de memoria de aplicación C # típico utilizado por el CLR en tiempo de ejecución.

Figura 4-2 . Un modelo típico de memoria de aplicaciones C #

El modelo de memoria CLR está estrechamente acoplado con los servicios de administración de memoria del sistema operativo. Para comprender el modelo de memoria CLR, es importante comprender el modelo de memoria del sistema operativo subyacente. También es crucial saber cómo el espacio de direcciones de la memoria física se abstrae en el espacio de direcciones de la memoria virtual, las formas en que la aplicación de usuario y el sistema de direcciones utilizan el espacio de direcciones virtuales, cómo funciona la asignación de direcciones de virtual a física, cómo funciona la memoria El archivo mapeado funciona, y así sucesivamente. Este conocimiento de fondo mejorará su comprensión de los conceptos del modelo de memoria CLR, incluidos AppDomain, stack y heap.

Para más información, consulte este libro:

C # Deconstruido: descubra cómo funciona C # en .NET Framework

Este libro + ClrViaC # + Windows Internals son recursos excelentes para el marco .net conocido en profundidad y en relación con el sistema operativo.




OK, simplemente y en pocas palabras, significan ordenado y no ordenado ...!

Pila : en los artículos de la pila, las cosas se superponen, significa que será más rápido y más eficiente de procesar ...

Así que siempre hay un índice para señalar el elemento específico, además, el procesamiento será más rápido, ¡también existe una relación entre los elementos! ...

Montón : No hay orden, el procesamiento será más lento y los valores se confunden sin orden o índice específico ... hay aleatorios y no hay relación entre ellos ... por lo que la ejecución y el tiempo de uso podrían variar ...

También creo la imagen de abajo para mostrar cómo pueden verse:




Pila , pila y datos de cada proceso en memoria virtual:




Tengo algo que compartir con ustedes, aunque los puntos principales ya están escritos.

Apilar

  • Acceso muy rápido.
  • Almacenado en la memoria RAM.
  • Las llamadas de función se cargan aquí junto con las variables locales y los parámetros de función que se pasan.
  • El espacio se libera automáticamente cuando el programa sale de un ámbito.
  • Almacenado en memoria secuencial.

Montón

  • Acceso lento comparativamente a la pila.
  • Almacenado en la memoria RAM.
  • Las variables creadas dinámicamente se almacenan aquí, que más tarde requieren liberar la memoria asignada después de su uso.
  • Se almacena donde se realiza la asignación de memoria, accediendo siempre por puntero.

Nota interesante:

  • Si las llamadas de función se hubieran almacenado en el montón, habrían resultado en 2 puntos desordenados:
    1. Debido al almacenamiento secuencial en la pila, la ejecución es más rápida. El almacenamiento en el montón habría provocado un gran consumo de tiempo, por lo que el programa completo se ejecutaría más lentamente.
    2. Si las funciones se almacenaron en el montón (almacenamiento desordenado apuntado con un puntero), no habría habido manera de volver a la dirección de la persona que llama (la pila da debido al almacenamiento secuencial en la memoria).

Los comentarios son bienvenidos.




Related