dominar la herramienta más potente (y temida) de C. Si entiendes este módulo, entiendes C.
Incluye 4 ejercicios con pistas y solución y un quiz de 6 preguntas. Tiempo estimado: 100 minutos.
1. La memoria es una fila de casillas numeradas
Cada byte tiene una dirección. Una variable int x = 42; ocupa 4 bytes en alguna dirección.
Un puntero es una variable cuyo valor es una dirección.
int x = 42;
int *p = &x; // p guarda la dirección de x ("p apunta a x")
printf("%d\n", *p); // 42 -> *p = "el valor al que apunta p" (desreferenciar)
*p = 100; // cambia x a 100 a través de p
printf("%d\n", x); // 100
x [ 100 ] dirección 0x1000
p [0x1000] ──────▶ apunta a x
&x→ "dirección de x".*p→ "lo que hay en la dirección p". (El*de la declaraciónint *psolo significa "p es puntero a int".)NULL= "no apunta a nada". Desreferenciarlo → segmentation fault. Inicializa los punteros:int *p = NULL;.
2. Por qué existen: modificar variables ajenas
Como los argumentos se copian, para que una función cambie algo del llamador le das su dirección:
void intercambiar(int *a, int *b) {
int t = *a;
*a = *b;
*b = t;
}
...
intercambiar(&x, &y);
¡Ahora sabes qué hace el & de scanf("%d", &n)! Le pasas dónde escribir.
También permite devolver varios valores (ejercicio 7.2).
3. Punteros y arrays: son casi lo mismo
El nombre de un array "decae" a un puntero a su primer elemento:
int v[4] = {10, 20, 30, 40};
int *p = v; // = &v[0]
p[2] == v[2] // 30
*(p + 2) == v[2] // 30 (p + 2 avanza 2 ENTEROS, no 2 bytes)
v[i] es, literalmente, azúcar para *(v + i). Aritmética de punteros: p + n avanza n * sizeof(*p) bytes; p2 - p1 da el nº de elementos entre ambos.
Una cadena es un char * que recorres hasta '\0':
int longitud(const char *s) { const char *p = s; while (*p) p++; return p - s; }
4. const con punteros
const int *p; // no puedes cambiar *p (lectura); sí cambiar p
int *const q = &x; // no puedes cambiar q; sí *q
Usa const char * / const int * en los parámetros que solo lees: documenta intención y el compilador te protege.
5. Los 4 pecados capitales
| Error | Qué pasa |
|---|---|
| Usar un puntero sin inicializar | apunta a cualquier sitio: crash o corrupción |
| Desreferenciar NULL | segmentation fault |
| Devolver la dirección de una variable local | puntero colgante: la variable ya murió |
| Salirte del array con aritmética | comportamiento indefinido |
int *mal(void) { int x = 5; return &x; } // ❌ x desaparece al salir
6. Punteros a punteros y a funciones (avance)
char **argves un array de cadenas: lo verás enmain(int argc, char **argv).- Un puntero a función permite pasar comportamiento como parámetro: así funciona
qsort(módulo 9).
Truco mental
Dibuja cajas y flechas. Siempre. Cada variable es una caja con su valor; cada puntero es una caja con una flecha. *p = seguir la flecha. &x = dibujar una flecha hacia x.
Prueba esto
- Imprime
x,&x,p,*p,&p(con%ppara direcciones:printf("%p", (void*)&x)). - Recorre un array con
for (int *p = v; p < v + n; p++). - Escribe
void a_mayusculas(char *s)que modifique la cadena en su sitio con un puntero.
Ejercicios prácticos del módulo 7
Programar se aprende programando: intenta cada ejercicio antes de mirar las pistas, y usa la solución solo para comparar. Marca los que resuelvas y tu progreso se guarda en este navegador.
7.1 swap con punteros
Entrada de ejemplo
3 8
Salida esperada
x=8 y=3
Tu punto de partida
#include <stdio.h>
void intercambiar(int *a, int *b) {
// TODO
}
int main(void) {
int x, y;
scanf("%d %d", &x, &y);
intercambiar(&x, &y);
printf("x=%d y=%d\n", x, y);
return 0;
}Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.
Pista 1
*a es 'el valor al que apunta a'. Necesitas una variable temporal.
Pista 2
int t = *a; *a = *b; *b = t;
Ver solución (inténtalo antes)
#include <stdio.h>
void intercambiar(int *a, int *b) {
int t = *a;
*a = *b;
*b = t;
}
int main(void) {
int x, y;
scanf("%d %d", &x, &y);
intercambiar(&x, &y);
printf("x=%d y=%d\n", x, y);
return 0;
}7.2 Devolver dos valores
Entrada de ejemplo
5 4 -2 9 0 3
Salida esperada
min=-2 max=9
Tu punto de partida
#include <stdio.h>
void min_max(const int *v, int n, int *min, int *max) {
// TODO
}
int main(void) {
int v[100], n, mn, mx;
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &v[i]);
min_max(v, n, &mn, &mx);
printf("min=%d max=%d\n", mn, mx);
return 0;
}Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.
Pista 1
Inicializa *min y *max con v[0] y recorre el resto.
Pista 2
Dentro de la funcion, siempre con *: *min = ..., if (v[i] < *min)
Ver solución (inténtalo antes)
#include <stdio.h>
void min_max(const int *v, int n, int *min, int *max) {
*min = *max = v[0];
for (int i = 1; i < n; i++) {
if (v[i] < *min) *min = v[i];
if (v[i] > *max) *max = v[i];
}
}
int main(void) {
int v[100], n, mn, mx;
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &v[i]);
min_max(v, n, &mn, &mx);
printf("min=%d max=%d\n", mn, mx);
return 0;
}7.3 Aritmetica de punteros
Entrada de ejemplo
6 1 2 3 4 5 6
Salida esperada
total=21 mitad=6
Tu punto de partida
#include <stdio.h>
int suma(const int *ini, const int *fin) {
// TODO
return 0;
}
int main(void) {
int v[100], n;
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &v[i]);
printf("total=%d\n", suma(v, v + n));
printf("mitad=%d\n", suma(v, v + n / 2));
return 0;
}Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.
Pista 1
p++ avanza al SIGUIENTE int (no al siguiente byte): el compilador multiplica por sizeof(int).
Pista 2
*p da el valor al que apunta p.
Ver solución (inténtalo antes)
#include <stdio.h>
int suma(const int *ini, const int *fin) {
int s = 0;
for (const int *p = ini; p < fin; p++) s += *p;
return s;
}
int main(void) {
int v[100], n;
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &v[i]);
printf("total=%d\n", suma(v, v + n));
printf("mitad=%d\n", suma(v, v + n / 2));
return 0;
}7.4 Tu propio strlen y strcpy
Entrada de ejemplo
hola
Salida esperada
longitud=4 copia=hola
Tu punto de partida
#include <stdio.h>
int mi_strlen(const char *s) {
// TODO
return 0;
}
void mi_strcpy(char *destino, const char *origen) {
// TODO
}
int main(void) {
char a[100], b[100];
scanf("%99s", a);
mi_strcpy(b, a);
printf("longitud=%d copia=%s\n", mi_strlen(a), b);
return 0;
}Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.
Pista 1
La cadena acaba en '\0', que vale 0 = falso: while (*p) p++;
Pista 2
La longitud es la distancia entre el final y el inicio: p - s.
Pista 3
strcpy: while ((*d = *o) != '\0') { d++; o++; } y despues *d = '\0'; (o la version compacta con post-incremento).
Ver solución (inténtalo antes)
#include <stdio.h>
int mi_strlen(const char *s) {
const char *p = s;
while (*p) p++;
return (int)(p - s);
}
void mi_strcpy(char *destino, const char *origen) {
while ((*destino++ = *origen++))
;
}
int main(void) {
char a[100], b[100];
scanf("%99s", a);
mi_strcpy(b, a);
printf("longitud=%d copia=%s\n", mi_strlen(a), b);
return 0;
}Quiz del módulo 7
6 preguntas para comprobar que has fijado los conceptos. Las opciones se barajan cada vez.