Update PEC2

This commit is contained in:
Manuel Vergara 2024-03-18 23:14:37 +01:00
parent d8a342aed9
commit fb8bfd3a8e

View File

@ -64,33 +64,138 @@ int main() {
### 4.2. Memoria estática
Cuando ejecutamos un programa, el sistema operativo busca una zona en la memoria donde este quepa. Por tanto, la dirección inicial de nuestro programa cambiará cada vez que lo ejecutemos.
args - Donde se guardan los argumentos de la línea de comandos.
BSS - Donde se guardan variables globales y estáticas sin iniciarlizarse en la declaración. Valores a 0
DS (Data Segment) - Donde se guardan las variables globales y estáticas inicializadas en la declaración.
text
Por ejemplo:
```c
#include <stdio.h>
int a; // BSS
int b=2; // DS
int main() {
static char c; // BSS
const float d=3.14; // DS
return 0;
}
```
La memoria de BBS y DC se reserva en tiempo de compilación, con lo cual es fija y por eso se conoce como memoria estática.
### 4.3 Relación entre objetos y memoria
Cuando declaramos un objeto, ya sea variable o constante, el nombre que le damos queda vinculado con la dirección de la posición de memoria donde este comienza.
Para saber la dirección donde ha quedado guardada una variable, lo podemos hacer con el operador &. Por ejemplo:
```c
#include <stdio.h>
long b;
int c;
int main() {
int a=0;
float d=5.0;
printf("Var %c (%d bytes) starts at %ld and ends at %ld\n", 'a', sizeof(a), (unsigned long)(&a), (unsigned long)
(&a)+(sizeof(a)-1));
printf("Var %c (%d bytes) starts at %ld and ends at %ld\n", 'b', sizeof(b), (unsigned long)(&b), (unsigned long)
(&b)+sizeof(b)-1);
printf("Var %c (%d bytes) starts at %ld and ends at %ld\n", 'c', sizeof(c), (unsigned long)(&c), (unsigned long)
(&c)+sizeof(c)-1);
printf("Var %c (%d bytes) starts at %ld and ends at %ld\n", 'd', sizeof(d), (unsigned long)(&d), (unsigned long)
(&d)+sizeof(d)-1);
return 0;
}
```
Por ejemplo, si queremos hacer la representación gráfica de la memoria para el siguiente programa:
```c
#include <stdio.h>
long b;
int c;
int main() {
int a=5;
short b=7;
char c='a';
return 0;
}
```
Tendríamos un resultado similar al siguiente (los valores binarios que hay no son los correctos):
| address | ... | X-1 | X-2 | X-3 | X-4 | X-5 | X-6 | X-7 | X-8 | ... |
| ------- | --- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | --- | --- |
| value | ... | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | | ... |
| | ... | a | a | a | a | b | b | c | | |
### 4.4. El tipo puntero en C
Los punteros son variables que en vez de contener un valor (como los enteros, caracteres o reales), contienen una dirección de memoria.
Un puntero es una variable cuyo valor es una dirección de memoria. Dado que un puntero contiene una dirección de memoria, el espacio que ocupa en memoria será siempre el mismo, el tamaño de una dirección. Esto dependerá si el sistema es de 32 o 64 bits, pero será independiente del tipo que le asignemos al puntero.
#### 4.4.1. Declaración
Al igual que cualquier otro variable, un puntero se declarará para poderlo utilizar:
```c
#include <stdio.h>
int main() {
/* Variable definition */
type *p=NULL;
}
```
Donde type es cualquier tipo de datos (entero, real, etc.). La constante NULL tiene un valor 0, y se interpreta como que este puntero no ha sido inicializado, o sea, que no apunta a ningún sitio.
#### 4.4.2. Operaciones
Aunque las direcciones son iguales sea cual sea el tipo, se le asigna un tipo para indicar qué contiene la dirección que se guarda en este puntero. Hay que tener mucho cuidado con los tipos de los punteros, aunque los punteros de todos los tipos ocupan lo mismo, el tipo asignado a un puntero tiene un gran efecto en el resultado que se obtiene al operar con él.
Dado que un puntero contiene una dirección de memoria, que es un valor numérico, podemos realizar las mismas operaciones que con cualquier valor numérico.
##### 4.4.2.1. Asignación
Si queremos que un puntero apunte a una determinada variable, o sea, asignar al puntero la dirección de una determinada variable, lo haremos de la siguiente manera (fijaos en que los tipos deben coincidir entre variable y puntero):
```c
#include <stdio.h>
int main() {
/* Variable definition */
int a=3;
float b=4.5;
int *pa=&a;
float *pb=&b;
}
```
En este ejemplo, a la variable `pa` se le asigna la dirección de la variable `a`, y a la variable `pb` la dirección de la variable `b`.
##### 4.4.2.2. Contenido
La dirección de una variable es una información que por sí sola no nos aporta gran cosa, ya que cada vez que ejecutamos nuestro programa este valor cambiará. Lo realmente importante es el hecho de que a partir de esta dirección podemos acceder al contenido que se guarda en esa posición de memoria.
El siguiente código muestra el operador contenido, que nos permite recuperar el contenido a partir del puntero.
```c
#include <stdio.h>
int main() {
/* Variable definition */
int a=5;
int *pa=NULL;
pa=&a;
*pa=25;
}
```
El puntero solo contiene la dirección inicial y el tipo es lo que nos permitirá saber cuántos bytes hay que utilizar y cómo interpretarlos. A continuación, se muestra una representación de la memoria en diferentes situaciones:
1. El estado inicial de las variables.
2. El resultado de asignar la dirección de `a` al puntero `pa`.
3. Cambiar el contenido del puntero 'pa', asignándole un nuevo valor.
Hay que tener presente que el contenido de un puntero a entero es un entero y, por lo tanto, se puede asignar a una variable de tipo entero y se le puede aplicar cualquier operación entera, como por ejemplo, sumarle otro entero o escribirlo por pantalla. Lo mismo ocurre con cualquier otro tipo, o sea, el contenido de un puntero a real es un real y por lo tanto lo podemos tratar como tal.