¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Punteros en C: dirección y desreferencia

Después de esta lección: Puedo leer y escribir código en C que usa punteros.

Lección de 55 min10 comandosLaboratorio de compiladores y arquitectura: gcc, flex, bison y RISC-V en una VM

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

PalabraQué significaEjemplo
Punterouna variable que guarda una direcciónint *p
Operador &da la dirección de una variable&precio
Desreferencialeer o escribir el dato al que apunta un puntero*p
Aritmética de punterossumar a un puntero avanza elementos, no bytes
NULLel puntero que no apunta a nada
Paso por valorla 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ónQué valeQué es
precio20, y luego 25un int
&preciosu dirección, un número que cambia en cada ejecuciónun puntero a int
plo mismo que &precioun puntero a int
*plo mismo que precioun int
*p = *p + 5precio pasa de 20 a 25una escritura por el puntero

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Entra en la carpeta de trabajo

Comando
mkdir -p ~/c && cd ~/c

Se 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

Comando
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.c

Se 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

Comando
gcc -Wall -o ptr ptr.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Ejecuta ptr

Comando
./ptr

Se 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

Código: python
# 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

Comando
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.c

Se 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

Comando
gcc -Wall -o arit arit.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Ejecuta arit

Comando
./arit

Se 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

Comando
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.c

Se 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

Comando
gcc -Wall -o val val.c

Se 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

Comando
./val

Se 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.

Sobre este laboratorio y su código: Laboratorio de compiladores y arquitectura: gcc, flex, bison y RISC-V en una VM

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.