Curso-lenguaje-C/fundamentos-programacion/PEC5/README.md
2024-05-07 21:20:14 +02:00

6.6 KiB

PEC 5

Volver a la página principal de "Fundamentos de la Programación"

Índice

13. Punteros 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. Es decir, un puntero es una variable que apunta a otra variable.

13.1. Uso de punteros

Dado que el puntero contiene una dirección de memoria, el espacio que ocupa en memoria es fijo. Este tamaño depende de la arquitectura del sistema, pero suele ser de 4 bytes en sistemas de 32 bits y de 8 bytes en sistemas de 64 bits.

13.1.1. Declaración

Al igual que cualquier otra variable, el puntero se debe declarar:

#include <stdio.h>

int main(int argc, char** argv) {
    /* Variable definition */
    type *p;
    /* Variable initialization */
    p = NULL;
}

type puede ser cualquier tipo de dato. La constante NULL tiene un valor 0, y se interpreta como que este puntero no ha sido inicializado. Es una buena práctica inicializar los punteros a NULL para evitar errores.

13.1.2. Operaciones

A los punteros se les asigna un tipo para que el compilador pueda saber cuántos bytes tiene que leer o escribir. Por ejemplo, si se declara un puntero a un entero, el compilador sabe que tiene que leer o escribir 4 bytes. El tipo asignado al puntero tiene un gran efecto en el resultado que se obtiene al operar con él.

Con un puntero podemos realizar las mismas operaciones que con cualquier valor numérico: Asignación, contenido, operaciones lógicas y aritméticas.

13.1.2.1. Asignación
#include <stdio.h>

int main(int argc, char** argv) {
    /* Variable definition */
    int a;
    float b;
    int *pa;
    float *pb;

    /* Variable initialization */
    a = 3;
    b = 4.5;
    pa = &a;
    pb = &b;
}

En este caso, pa apunta a la dirección de memoria de a, y pb apunta a la dirección de memoria de b.

13.1.2.2 Contenido

La dirección de una variable nos permite acceder al contenido almacenado en esa posición de memoria. Aunque la dirección en si misma puede cambiar cada vez que el programa se ejecuta, lo relevante es que nos da acceso al contenido de la variable apuntada. Es decir, si tenemos la dirección guardada en pb, podemos recuperar el contenido de b con la variable apuntada por pb.

#include <stdio.h>

int main (int argc, char** argv) {
    /* Variable definition */
    int a;
    int *pa;

    /* Step 1, Variable initialization */
    a = 5;
    pa = NULL;
    /* Step 2, Assign the address of a to pa */
    pa = &a;
    /* Step 3, change the content of pa, new value */
    *pa = 25;
}

Cuando recuperamos el contenido el tipo de puntero es muy importante, ya que 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.

El contenido de un puntero a entero se puede asignar a una variable entera y se le puede aplicar cualquier operación entera. Lo mismo ocurre con cualquier otro tipo.

13.1.2.3. Operaciones lógicas

Cuando comparamos dos punteros como el siguiente ejemplo:

#include <stdio.h>
int main(int argc, char** argv) {
    /* Variable definition */
    int a;
    int b;
    int *pa;
    int *pb;

    /* Variable initialization */
    a = 3;
    b = 5;
    pa = &a;
    pb = &b;

    /* Comparing memory addresses */
    if (pa < pb) {
        printf("pa is smaller than pb\n");
    }
    return 0;
}

Comparamos las direcciones que guardan, no los valores de las variables a las que apuntan.

Para comparar el contenido de la dirección a la que apunta se tiene que utilizar el operador de indirección *.

#include <stdio.h>

int main(int argc, char** argv) {

    /* Variables definition */
    int a;
    int b;
    int *pa;
    int *pb;

    /* Variables initialization */
    a = 3;
    b = 5;
    pa = &a;
    pb = &b;

    /* Comparing the values pointed by pa and pb, not the memory addresses */
    if (*pa < *pb) {
        printf("the value pointed by pa is smaller than the value pointed by pb");
    }
    return 0;
}
13.1.2.4. Operaciones aritméticas

Entonces, las direcciones son valores numéricos y les podemos aplicar operadores aritméticos.

Una diferencia importante en el comportamiento de estos operadores cuando se aplican a punteros es que trabajan con unidades de datos, no con valores. Por ejemplo, si le sumamos 3 a un puntero entero que contiene la dirección 33, el resultado no será la dirección 36, sino que será 33 + 3 * tamaño(caracter) = 33 + 3 * 1 = 36. Por lo tanto, el puntero determina el resultado final de la operación.

13.1.3. Ejemplo de puntero

#include <stdio.h>

int main(int argc, char** argv) {
    /* Variable definition */
    int a;
    int b;
    int *p;

    /* Step 1, Variable initialization */
    a = 3;
    b = 5;
    p = NULL;

    /* Step 2, Assign the address of a to p */
    p = &a;

    /* Step 3, assign the sum of a and b to the content of p */
    *p = a+b;

    return 0;
}

Es importante entender la diferencia entre asignar un valor al puntero o asignarlo al contenido del puntero. Cuando asignamos el valor de a+b al contenido de p, estamos asignando el valor de a+b a la variable a, es decir, tendrá el valor de 8.

13.2. Punteros a tuplas

13.3. Punteros y vectores/matrices

14. Parámetros de entrada/salida en C

14.1. Ejemplo de parámetros de salida y entrada/salida en C

15. Modularidad en Codeline

15.1. Configuración inicial

15.2. Múltiples ficheros

Volver arriba