Los Segmentos de Memoria Cada vez que se ejecuta cualquier programa, el mismo deberá pasar a memoria. Los programas en memoria tienen varias secciones o segmentos, los cuales sirven para organizar el manejo de la memoria por el programa.
Segmentos de Memoria Los Segmentos de Memoria son: CS ? Code Segment (Segmento de Código) DS ? Data Segment (Segmento de Datos) SS ? Stack Segment (Segmento de Pila) HS ? Heap Segment (Segmento de Heap) Cada Segmento tiene asignado una “cosa” del programa
Segmentos de Memoria Segmento de Código (CS = Code Segment) Segmento de Datos (DS = Data Segment) Segmento de Pila (SS = Stack Segment) Segmento del Heap (HS = Heap Segment)
Segmento de Código En este segmento se guardan las instrucciones, en lenguaje máquina, de nuestro programa.
Segmento de Datos En este Segmento se guardan las variables globales del programa.
Segmento de Pila En este segmento se guardan: Los Llamados a las funciones: Los parámetros de las funciones llamadas Las variables locales Otra información necesaria para el funcionamiento del programa. Cada vez que se llama a una función entra en este segmento con toda su información.
Segmento del Heap En este segmento se guardan los objetos que han sido creados dinámicamente en tiempo de corrido.
El Segmento de Pila y el Llamado de Funciones (Call Stack) void a(); void b(); void c(); void d(); int main() { a(); // fin return 0; } void a(){ b(); c(); } void b(){ d(); } void c() {} void d() {} main() a() b() d() Al terminar la función d() ella sale del stack Al terminar la función b() ella sale del stack c() Al terminar la función c() ella sale del stack Al terminar la función a() ella sale del stack Al terminar el main() se acaba el programa
Parámetros por Valor y por Referencia Los parámetros, por defecto, son pasados a las funciones por valor. Cuando los parámetros son pasados por valor lo que sucede es que se hace una copia de lo que se manda. Los parámetros por referencia NO HACEN UNA COPIA, sino que se refieren a los objetos que fueron enviados como parámetro.
Parámetros por Valor void f(int x) { x = 5; } … int y = 10; f(y); cout << y << endl; La Memoria (Gp:) 10 (Gp:) y Al declarar y (Gp:) 10 (Gp:) x Al llamar a la función f, se hace una copia de y (Gp:) 5 (Gp:) x La función f, cambia el valor de x Al Terminar la función f: ¿Cuánto queda en la variable y?
Parámetros por Referencia void f(int& x) { x = 5; } … int y = 10; f(y); cout << y << endl; La Memoria (Gp:) 10 (Gp:) y Al declarar y x Al llamar a f crea una Referencia x 5 Cambia el valor de x
Recursión La Recursión es cuando una función se llama a si misma. Viene de las definiciones matemáticas, que por naturaleza son recursivas. Existen dos partes de una función recursiva: El Caso Base El Caso Recursivo
Las Partes… El Caso Base Por medio de este caso es que termina la función recursiva. Es indispensable para evitar la recursión infinita. El Caso Recursivo Es la parte de la función en la que se llama a la función otra vez.
Ejemplo de Hierro… La función factorial está definida matemáticamente:
Ahora, la función: long int factorial(unsigned int n) { if (n == 1 || n == 0) return 1; else return n * factorial(n-1); }
Prueba de Escritorio de factorial Vamos a suponer el llamado a: factorial(5) Segmento de Pila Llamados a Funciones Anteriores factorial(5) factorial(4) factorial(3) factorial(1) 1 6 24 120 5 == 0 || 5 == 1 NO 5 * factorial(4) 4 == 0 || 4 == 1 NO 4 * factorial(3) 3 == 0 || 3 == 1 NO 3 * factorial(2) factorial(2) 2 == 0 || 2 == 1 NO 2 * factorial(1) 1 == 0 || 1 == 1 ¡SI! 1 2
El Segmento de Pila Cuando se llama a una función recursiva, cada llamado que se haga irá entrando al segmento de pila. Si la recursión es demasiado “profunda”, corremos el riesgo de llenar la pila, y revalsarla: Este es un error de tiempo de corrido llamado Desbordamiento de Pila (Stack Overflow)