Una función que no puede bajar un precio
Un puntero en C es una variable que guarda la dirección de otra variable. Con él, una función puede cambiar el valor de la variable de quien la llama.
Una tienda tiene la función rebaja, que debe quitar cinco euros a un precio. Si recibe una copia del precio, el original no cambia nunca. Con un puntero, sí cambia.
Al final de esta lección
Nota
Puedo leer y escribir código en C que usa punteros.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Puntero | una variable que guarda una dirección | int *p |
| Operador & | da la dirección de una variable | &precio |
| Desreferencia | leer o escribir el dato al que apunta un puntero | *p |
| Aritmética de punteros | sumar a un puntero avanza elementos, no bytes | |
| NULL | el puntero que no apunta a nada | |
| Paso por valor | la función recibe una copia del argumento |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
& da la dirección y * busca el dato
Una variable ocupa bytes en la memoria, y esos bytes tienen una dirección. El operador & da esa dirección. Un puntero la guarda: int *p = &precio; declara un puntero a int que apunta a precio.
El operador * hace la desreferencia: va a la dirección y lee o escribe el dato que hay allí. Escribir *p = 25 cambia precio.
Ejemplo resuelto: precio y su puntero
| Expresión | Qué vale | Qué es |
|---|---|---|
| precio | 20, y luego 25 | un int |
| &precio | su dirección, un número que cambia en cada ejecución | un puntero a int |
| p | lo mismo que &precio | un puntero a int |
| *p | lo mismo que precio | un int |
| *p = *p + 5 | precio pasa de 20 a 25 | una escritura por el puntero |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Entra en la carpeta de trabajo
mkdir -p ~/c && cd ~/cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe ptr.c: un precio y un puntero
printf '#include <stdio.h>\nint main(void) {\n int precio = 20;\n int *p = &precio;\n *p = *p + 5;\n printf("%%d %%d\\n", precio, *p);\n return 0;\n}\n' > ptr.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Compila ptr.c con avisos
gcc -Wall -o ptr ptr.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecuta ptr
./ptrSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Sumar uno a un puntero avanza un elemento
Un vector guarda sus elementos uno tras otro. Si p apunta al primero, p + 1 apunta al segundo.
El compilador suma el tamaño del tipo, no un byte. Con enteros de 4 bytes, p + 1 está cuatro direcciones más allá. Es la aritmética de punteros, y p[3] es otra forma de escribir *(p + 3).
Las direcciones de tres enteros seguidos
# La memoria como una lista de bytes: una dirección es un índice.
memory = [0] * 16
# Guardamos tres enteros de 4 bytes: 10, 20 y 30 (un byte basta, el resto son ceros).
memory[0] = 10
memory[4] = 20
memory[8] = 30
base = 0
for index in range(3):
address = base + index * 4
print("p +", index, "es la dirección", address, "y guarda", memory[address])
# Cambia el 4 por 1 y mira qué pasaría si un puntero a int avanzara un solo byte.
Escribe arit.c, que recorre un vector
printf '#include <stdio.h>\nint main(void) {\n int notas[4] = {10, 20, 30, 40};\n int *p = notas;\n printf("%%d %%d %%d\\n", *p, *(p + 2), p[3]);\n printf("%%ld\\n", (long)((char *)(p + 1) - (char *)p));\n return 0;\n}\n' > arit.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Compila arit.c
gcc -Wall -o arit arit.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecuta arit
./aritSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Una función cambia lo que recibe por dirección
En C, una función recibe una copia de cada argumento: el paso por valor. Cambiar la copia no toca el original.
Para que rebaja cambie el precio, le pasas su dirección: rebaja(&precio). Dentro, *n = *n - 5 escribe en el original. Un puntero a NULL no apunta a nada, y leerlo rompe el programa.
Escribe val.c con dos funciones
printf '#include <stdio.h>\nvoid sube(int n) { n = n + 1; }\nvoid sube_ptr(int *n) { *n = *n + 1; }\nint main(void) {\n int x = 5;\n sube(x);\n printf("%%d\\n", x);\n sube_ptr(&x);\n printf("%%d\\n", x);\n return 0;\n}\n' > val.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Compila val.c
gcc -Wall -o val val.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecuta val: 5 y luego 6
./valSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un error frecuente
Advertencia
Mucha gente cree que p + 1 suma un byte a la dirección. Suma el tamaño del tipo al que apunta p. Con un puntero a int, avanza 4 bytes; con uno a char, 1. Por eso p + 1 siempre cae en el elemento siguiente.