12 - Punteros - Introducción
Objetivos
Introducir punteros en C como direcciones de memoria que permiten trabajar sobre datos existentes.
- Distinguir entre el valor de una variable y su dirección
- Usar el operador
¶ obtener la dirección de una variable - Declarar punteros básicos con
tipo *nombre - Usar el operador
*para acceder o modificar el dato apuntado - Relacionar punteros con
scanfy el paso por valor - Usar punteros a estructuras y el operador
->
Contenidos
Valor y dirección
- Valor: dato guardado dentro de una variable
- Dirección: ubicación donde vive esa variable durante la ejecución
- Operador
&: obtiene la dirección de una variable
Punteros básicos
- Definición: un puntero es una variable cuyo valor es una dirección de memoria
- Declaración:
int *pdeclara un puntero aint - Inicialización: antes de usar un puntero debe apuntar a una dirección válida
- Dereferencia:
*paccede al dato almacenado en la dirección guardada porp
Funciones y direcciones
- Paso por valor: C sigue copiando los argumentos en los parámetros
- Dirección como valor: una función puede recibir una copia de una dirección
- Modificación: usando
*p, la función puede modificar el dato original - Relación con
scanf:scanfnecesita una dirección porque escribe dentro de una variable existente
Punteros a estructuras
- Tipo:
struct Alumno *representa un puntero a una estructuraAlumno - Acceso:
p->campoaccede a un campo de la estructura apuntada - Equivalencia:
p->campoes una forma más cómoda de escribir(*p).campo - Aplicación: funciones como
cargar_alumno(struct Alumno *a)pueden completar una estructura recibida por dirección
Errores frecuentes
- Usar un puntero sin una dirección válida
- Confundir el puntero
pcon el dato apuntado*p - Olvidar
&al llamar una función que espera un puntero - Usar
.en lugar de->con un puntero a estructura
Material de Clase
Presentaciones
Práctico
Ver ejercicios
Los ejercicios de esta semana introducen punteros de forma gradual. Conviene resolverlos en orden: primero se trabaja con variables simples, luego con funciones que modifican datos existentes y al final con estructuras.
Cuando se imprima una dirección con %p, convertirla a void *:
printf("%p\n", &x);
Parte A: valor, dirección y punteros básicos
Esta parte distingue el dato guardado en una variable de la dirección donde ese dato vive durante la ejecución.
Ejercicio 1: Valor y dirección
Enunciado: Declarar una variable int x, asignarle un valor e imprimir en
pantalla su valor y su dirección.
Ejemplo: Si x vale 10, el programa debe imprimir 10 y luego una
dirección de memoria.
Ejercicio 2: Primer puntero
Enunciado: Declarar una variable int x = 10 y un puntero int *p que
apunte a x. Imprimir:
- El valor de
x. - La dirección de
x. - La dirección guardada en
p. - El valor obtenido con
*p.
Pregunta para responder en un comentario: Qué relación hay entre &x y
p?
Ejercicio 3: Modificar usando *p
Enunciado: Declarar una variable int numero = 5 y un puntero que apunte a
ella. Usar el puntero para cambiar el valor de numero a 20 e imprimir el
valor final de la variable.
Pregunta para responder en un comentario: Qué cambia si se escribe p = &x
y qué cambia si se escribe *p = 20?
Ejercicio 4: Puntero sin inicializar
Enunciado: El siguiente fragmento es incorrecto. Explicar cuál es el problema y escribir una versión corregida usando una variable válida.
int *p;
*p = 10;
Parte B: funciones que modifican datos existentes
Esta parte usa punteros para que una función pueda modificar una variable
creada en main().
Ejercicio 5: Duplicar un valor
Enunciado: Escribir una función void duplicar(int *x) que multiplique por
2 el valor original recibido. En main, leer un número, llamar a la función
e imprimir el valor modificado.
Pregunta para responder en un comentario: Por qué la llamada debe usar
duplicar(&numero) y no duplicar(numero)?
Ejercicio 6: Incrementar en una unidad
Enunciado: Escribir una función void incrementar(int *x) que aumente en
1 el valor apuntado por x. En main, declarar un entero, mostrarlo antes y
después de llamar a la función.
Ejercicio 7: Intercambiar dos valores
Enunciado: Escribir una función void intercambiar(int *a, int *b) que
intercambie los valores de dos variables enteras. En main, declarar dos
enteros, imprimirlos antes de la llamada, llamar a la función e imprimirlos
después.
Condición: Dentro de la función se puede usar una variable auxiliar de tipo
int.
Ejercicio 8: Cargar un entero con una función
Enunciado: Escribir una función void cargar_entero(int *x) que lea un
entero con scanf y lo guarde en la variable recibida por dirección. En
main, declarar una variable, llamar a la función e imprimir el valor leído.
Pregunta para responder en un comentario: En la función, por qué se usa
scanf("%d", x) y no scanf("%d", &x)?
Ejercicio 9: Corregir paso por valor
Enunciado: El siguiente programa intenta cambiar el valor de una variable,
pero al final imprime 5. Explicar el problema y corregirlo usando un puntero.
void cambiar(int n) {
n = 8;
}
int main() {
int x = 5;
cambiar(x);
printf("%d\n", x);
return 0;
}
Parte C: punteros a estructuras
Esta parte retoma struct para practicar funciones que actualizan campos de
una estructura existente.
Usar esta definición en los ejercicios de la parte C, salvo que el enunciado indique otra cosa:
struct Alumno {
char nombre[20];
int edad;
float promedio;
};
Ejercicio 10: Puntero a un alumno
Enunciado: Declarar una variable struct Alumno inicializada con llaves y
un puntero que apunte a ella. Mostrar la edad del alumno usando primero
(*p).edad y luego p->edad.
Pregunta para responder en un comentario: Por qué las dos expresiones muestran el mismo valor?
Ejercicio 11: Cumplir años
Enunciado: Escribir una función void cumplir_anios(struct Alumno *a) que
aumente en 1 la edad del alumno recibido. En main, crear un alumno, mostrar
su edad antes y después de llamar a la función.
Ejercicio 12: Cargar un alumno
Enunciado: Escribir una función void cargar_alumno(struct Alumno *a) que
lea desde teclado el nombre, la edad y el promedio de un alumno. En main,
declarar un alumno, cargarlo con la función y luego imprimir sus campos.
Nota: Para leer el nombre, usar scanf("%19s", a->nombre).
Ejercicio 13: Cargar un grupo
Enunciado: Declarar un arreglo de 4 alumnos. Recorrer el arreglo con un
for y cargar cada posición llamando a cargar_alumno. Luego recorrerlo de
nuevo para imprimir todos los alumnos.
Ejercicio 14: Análisis de errores
Enunciado: Indicar qué error hay en cada fragmento y escribir la versión corregida.
int x = 5; int *p = &x; p = 8;void cambiar(int *p); int x = 5; cambiar(x);void cargar(struct Alumno *a) { a.edad = 20; }scanf("%d", a->edad);
Recursos
Para Practicar
Bibliografía
- “El lenguaje de programación C” - Kernighan & Ritchie (Capítulo 5)
- “Cómo programar en C/C++” - Deitel & Deitel