Documentos de Académico
Documentos de Profesional
Documentos de Cultura
T11 Punteros
T11 Punteros
PUNTEROS
Concepto de puntero ................................................................................................................................................ 2
Acceso desde un puntero a su variable apuntada ................................................................................................... 3
Iniciación de un puntero a NULL ........................................................................................................................... 4
Operaciones con punteros ....................................................................................................................................... 5
Constante puntero y puntero a constante ............................................................................................................... 7
Prioridad de los operadores *, ++ y -- sobre el mismo puntero.............................................................................. 7
Punteros y arrays ..................................................................................................................................................... 9
Punteros y vectores ............................................................................................................................................... 9
0022FFEE ............................................................................................................................................................... 9
Vectores de punteros y punteros a vectores ........................................................................................................ 11
Punteros y cadenas de caracteres....................................................................................................................... 11
Punteros y tablas................................................................................................................................................. 12
0022FFDE ............................................................................................................................................................. 12
Ejercicios resueltos ................................................................................................................................................ 14
Ejercicio 1.- Mapa de memoria .......................................................................................................................... 14
Ejercicio 2.- Tabla de direcciones de memoria .................................................................................................. 15
Ejercicio 3.- Recorrido vector con notación punteros ........................................................................................ 16
Ejercicio 4.- Recorrido tabla con notación punteros ......................................................................................... 18
Ejercicio 5.- Lectura elementos vector con notación punteros .......................................................................... 20
Ejercicio 6.- Lectura elementos tabla con notación punteros ............................................................................ 21
Programas complementarios ................................................................................................................................. 22
Ejercicios propuestos ............................................................................................................................................. 23
Ejercicio 7.- Acceder a una variable mediante un puntero ................................................................................ 23
Ejercicio 8.- Ordenar vector de claves sin modificarlo ...................................................................................... 24
Ejercicio 9.- Mapa de caracteres........................................................................................................................ 27
Ejercicio 10.- Recorrido de arrays con notación puntero .................................................................................. 27
Ejercicio 11.- Manejo de una cadena con un puntero a char ............................................................................ 29
Ejercicio 12.- Submatrices simétricas ................................................................................................................ 29
Ejercicio 13.- Rotar una matriz con punteros .................................................................................................... 31
Nota: El tamaño del tipo int y del tipo puntero depende de la plataforma y del compilador. Valores típicos son 2
bytes en entornos MS-DOS y 4 bytes para entorno Windows ó Linux. En los ejemplos de este trma, salvo que se
indique explícitamente lo contrario, se está suponiendo la ocupación del tipo int y del tipo puntero de 4 bytes.
De todas formas, son fácilmente adaptables todos los ejemplos al caso de compiladores con el tipo int y tipo
puntero de 2 bytes.
Concepto de puntero
Cuando una variable se declara, se asocian tres atributos fundamentales a la misma:
− Su nombre
− Su tipo
− Sus direcciones en memoria
Al valor o contenido de una variable se accederá normalmente a través de su nombre. Pero,
como veremos más adelante, también podrá accederse al contenido de la misma si conocemos
su dirección de comienzo en memoria
A las constantes y expresiones no puede aplicarse el operador dirección (&), sólo a las
variables.
1
Por supuesto que como los punteros son a su vez variables, están almacenados en algún lugar de la memoria y
tienen su propia dirección
Nombre Dirección
variable comienzo
123.456
f 0022FF44 Direcciones
de memoria
crecientes
0022FF4
punt 0022FF14 4
Un puntero es una verdadera variable, por lo tanto puede cambiar de valor, es decir, después
de haber asignado una dirección a un puntero, durante la ejecución del programa se le puede
volver a asignar la dirección de otra variable, apuntando por tanto a esa nueva variable y
abandonando la anterior. Pero siempre debe mantenerse la condición de que el tipo de dato de
la variable que se va a apuntar sea del mismo tipo que el del puntero. En caso contrario puede
haber pérdida de información sin que el compilador emita ningún mensaje de error.
2
El tipo puntero ocupará 2 o 4 bytes, dependiendo del compilador y la plataforma. En todos los ejemplos de este
documento, salvo que se especifique lo contrario, se considera que el tipo puntero ocupa 4 bytes.
Por ejemplo,
int a = 44, b = 125;
...
int *pnum;
...
pnum = &a;
*pnum = 145;
...
printf("a = %d b = %d\n", a, b);
printf("La dirección de a es %p\n", pnum);
printf("El valor de a es %d\n", *pnum);
printf("La dirección de b es %p\n", &b);
Salida en pantalla:
a = 145 b = 125
La dirección de a es 0022FF44
El valor de a es 145
La dirección de b es 0022FF40
nombre dirección
variable comienzo
145 Direcciones
a 0022FF44 de memoria
crecientes
125
b 0022FF40
0022FF44
pnum
indeterminado. Para no confundir éste valor indeterminado con ninguna dirección de memoria
válida, se puede iniciar el puntero a valor nulo (constante NULL). Este valor indicará que el
puntero todavía no apunta a ninguna dirección de memoria válida.
nombrepuntero = NULL;
¡¡¡IMPORTANTE!!! Jamás se debe dejar un puntero sin iniciar, es decir, sin apuntar a una
variable en concreto o a NULL. Nunca hacer lo siguiente:
int *ptr;
*ptr = 10;
ptr está apuntando a cualquier parte, código, datos, pudiendo producirnos el error en
ejecución, puesto que en compilación esto no nos da error, ni siquiera advertencia.
Comparación de punteros
Es posible comparar dos punteros del mismo tipo con el operador igualdad (==), para
comprobar si contienen la misma dirección de memoria, es decir si apuntan a la misma
variable. También es posible utilizar los operadores relacionales >, >=, <, <= con dos
punteros para comparar las posiciones relativas en memoria que ocupan las variables
apuntadas por ellos.
Aritmética de punteros
En C sólo se permiten dos operaciones aritméticas con punteros: adición y sustracción de un
entero a un puntero, y sustracción de punteros
Por ejemplo, observaremos que incrementar o decrementar en una unidad un puntero a int
significará avanzar dos posiciones de memoria.
a[3] 0022FF46 1
printf("*pte: %hd\n", *pte);
pte++; Direcciones
a[2] 0022FF44 10 de memoria
printf("*pte: %hd\n", *pte);
pte = pte + 2; crecientes
printf("*pte: %hd\n", *pte); a[1] 0022FF42 100
pte = pte - 3;
printf("*pte: %hd\n", *pte); a[0] 0022FF40 1000
Salida en pantalla:
*pte: 1000
*pte: 100
pte 0022FF0C 0022FF4
*pte: 1
0
*pte: 1000
float lista[10];
float *pt1 = &lista[2];
float *pt2 = &lista[7];
...
int d;
3
O dicho de otra forma, el resultado de la resta de dos punteros es la “distancia” entre las direcciones apuntadas
por ellos, no en bytes, sino en datos del tipo de los punteros
d = pt2 - pt1;
Punteros y arrays
Existe una relación muy estrecha entre arrays y punteros. Como punto de partida, el nombre
de un array es un puntero constante que contiene la dirección del primer elemento de
dicho array.
Punteros y vectores
El nombre del vector (sin índice ni corchetes) es un puntero constante que contiene la
dirección del primer elemento del vector.
Por ejemplo,
short lista[4] = {1000, 100, 10, 1};
0022FFEE
Son equivalentes...
§ Dos vectores no pueden copiarse4 directamente uno en otro, sino que hay que copiarlos
elemento a elemento.
§ Dos vectores no pueden compararse5 directamente para ver si son iguales (contienen los
mismos valores en los elementos de posiciones equivalentes), sino que hay que
compararlos elemento a elemento.
Con el nombre del vector, podemos utilizar bien notación subíndice o bien notación puntero
para acceder a los elementos individuales. Igualmente se puede poner índices a los punteros
que apuntan a vectores. Por ejemplo,
int cad[10];
int *p; /* Definición de un puntero a entero */
int t;
p = cad; /* El puntero p apunta al primer elemento del vector */
4
vector1 = vector2 es ilegal porque vector1 es un puntero constante
5
vector1 == vector2 no compara contenidos sino la dirección de memoria de comienzo de los vectores, por lo
que siempre el resultado será “falso”
Vector de punteros
tipo_dato vector[N]; define un vector de N elementos
tipo_dato *vector[N]; define un vector de N elementos que son punteros
Por ejemplo,
int *ptr[10];
define un vector de 10 punteros a int. Cada elemento del vector prt será un puntero, por lo
que serán válidas sentencias del tipo
ptr[3] = &var; donde var será una variable int
ptr[4] = NULL; iniciamos a NULL
Puntero a un vector
tipo_dato (*puntero)[N]; define un puntero a un vector de N elementos
tipo_dato *(*puntero)[N]; define un puntero a un vector de N punteros
Por ejemplo,
int (*ptr)[10]; define un puntero a un vector de 10 int
int *(*ptr)[10]; define un puntero a un vector de 10 punteros a int
Punteros y tablas
Podremos pensar en una tabla de f filas y c columnas como un vector de f elementos, donde
cada elemento es a su vez un vector de c elementos.
short mat[4][3] = {0, 1, 2, 10, 11, 12, 20, 21, 22, 30, 31, 32};
El nombre de la tabla (sin índices ni corchetes) será por tanto una puntero constante que
contiene la dirección del primer elemento, es decir de la primera fila de la tabla. Por tanto será
un puntero constante a un vector.
elemento direcciones
tabla mat
0022FFF5 32
mat[3][2] 0022FFF4
0022FFF3 31
mat[3][1] 0022FFF2 fila 3
0022FFF1 30
mat[3][0] 0022FFF0
0022FFEF 20
mat[2][2] 0022FFEE
0022FFED 21 Direcciones
fila 2 de memoria
mat[2][1] 0022FFEC
0022FFEB crecientes
20
mat[2][0] 0022FFEA
0022FFE9 12
mat[1][2] 0022FFE8
0022FFE7 11
mat[1][1] 0022FFE6 fila 1
0022FFE5 10
mat[1][0] 0022FFE4
0022FFE3 2
mat[0][2] 0022FFE2
0022FFE1 1
mat[0][1] 0022FFE0
mat 0022FFDF 0 fila 0
0022FFDE mat[0][0] 0022FFDE
6
tabla1 = tabla2 es ilegal porque tabla1 es un puntero constante
7
tabla1 == tabla2 no compara contenidos sino la dirección de memoria de comienzo de las tablas, por lo que
siempre el resultado será “falso”
Ejercicios resueltos
NOTA: Al ejecutar el programa, los valores absolutos de las direcciones que se presentan en
pantalla pueden variar respecto a las presentadas en el anterior ejemplo. Lo importante no es
el valor absoluto de la dirección, sino observar el incremento en posiciones de memoria que
se produce en la disposición de unas variables y otras.
Igualmente, el tamaño del tipo int y del tipo puntero puede variar dependiendo del
compilador. En el ejemplo ambos se consideran de 4 bytes.
Suponiendo que las direcciones de comienzo de las variables lista, numero y letra son
respectivamente 0022FED0, 0022FECC y 0022FEC3, y que tanto el tipo int como el tipo
puntero ocupan 4 bytes, indica...
1. El valor de &numero
2. Qué valor es asignado a pc
3. El valor de *pf
4. El valor de lista
5. El valor de &lista[0]
6. El valor de *lista
7. El valor de lista[0]
8. El valor de *(lista+2)
9. El valor de *lista+2
10. El valor de &lista[2]
Solución
Según nos indica el enunciado del problema, en teoría, el mapa de memoria de las anteriores
declaraciones será:
Variable Dirección comienzo Tamaño Valor
vector1[9] 0022FF28 4 0
vector1[8] 0022FF24 4 0
... ... ... ...
vector1[1] 0022FF08 4 20
vector1[0] 0022FF04 4 1.3
vector2[9] 0022FF00 4 0
vector2[8] 0022FEFB 4 0
... ... ... ...
vector2[1] 0022FEE0 4 0
vector2[0] 0022FEDB 4 0
lista[2] 0022FED8 4 30
#include <stdio.h>
#define DIM 10
void main(void) {
int k;
float v[DIM];
Solución
Una primera forma sería simplemente utilizar la notación de punteros sobre el propio nombre
del vector, recordando que en el caso de vectores v[k] es equivalente a *(v+k)
#include <stdio.h>
#define DIM 10
void main(void) {
int k;
float v[DIM];
Una segunda forma podría ser utilizar un puntero a float en el que cargamos la dirección de
comienzo del vector v, y el cual vamos incrementando para que vaya apuntando a cada uno de
los elementos del vector v.
#include <stdio.h>
#define DIM 10
void main(void) {
int k;
float v[DIM];
float *p;
p = v;
for (k=0; k<DIM; k++)
{ *p = k;
p++;
}
}
Las dos sentencias del cuerpo del bucle for, podrían sustituirse por simplemente *p++ = k;
Primero se evalúa el operador indirección (*) y posteriormente el post-incremento (++), por
ello primeramente se modifica la variable apuntada por el puntero p y posteriormente se
incrementa la dirección contenida en el puntero p.
#include <stdio.h>
#define FIL 10
#define COL 7
void main(void) {
int f, c;
double mt[FIL][COL];
...
for (f=0; f<FIL; f++)
{ for (c=0; c<COL; c++)
printf("%5.2lf", mt[f][c]);
printf("\n");
}
}
Solución
Una primera forma puede ser utilizar la notación de punteros sobre el propio nombre de la
tabla, recordando que en el caso de tablas mt[f][c] es equivalente a *(*(mt+f)+c)
...
for (f=0; f<FIL; f++)
{ for (c=0; c<COL; c++)
printf("%5.2lf", *(*(mt+f)+c));
printf("\n");
}
Una segunda forma puede ser utilizar la notación combinada de subíndice y puntero sobre el
propio nombre de la tabla, recordando que mt[f][c] es equivalente a *(mt[f]+c).
No se suele utilizar esta notación debido a que no aporta claridad, sino todo lo contrario.
...
for (f=0; f<FIL; f++)
{ for (c=0; c<COL; c++)
printf("%5.2lf", *(mt[f]+c));
printf("\n");
}
Ninguna de las dos notaciones anteriores se suele utilizar debido a que no aportan claridad,
sino todo lo contrario.
Sabiendo que una tabla se almacena en memoria de forma lineal fila a fila, comenzando la
primera fila en la posición más baja de memoria ocupada por la tabla, podemos presentar una
tercera forma de recorrerla. Para ello se definirá un puntero del tipo base de la tabla, que
apunte a su primer elemento, y se irá calculando la posición de memoria donde se van
encontrando cada uno de los elementos de la tabla, a base de sumar la longitud de las filas
desde el comienzo de la tabla y los elementos desde el comienzo de la fila donde está situado
el elemento al que se desea acceder.
Observemos que la asignación pmt = *mt es correcta debido a que *mt es un puntero al
primer elemento de la primera fila de la tabla, es decir, un puntero a double.
...
double *pmt;
...
pmt = *mt;
for (f=0; f<FIL; f++)
{ for (c=0; c<COL; c++)
printf("%5.2lf", *(pmt + f*COL + c));
printf("\n");
}
#include <stdio.h>
#define DIM 10
void main(void) {
int k;
float v[DIM];
...
for (k=0; k<DIM; k++)
{ printf("Elemento [%d]?: ", k+1);
scanf("%f", &v[k]);
}
...
}
Solución
Si recordamos que
&v[i] es equivalente a v+i
En el siguiente fragmento de código se leen los elementos de una tabla utilizando notación de
subíndices.
#include <stdio.h>
#define FIL 10
#define COL 7
void main(void) {
int f, c;
double mt[FIL][COL];
...
for (f=0; f<FIL; f++)
for (c=0; c<COL; c++)
{ printf("Elemento [%d][%d]?: ", f+1, c+1);
scanf("%lf", &mt[f][c]);
}
...
}
Solución
Si recordamos que
mt[f][c] es equivalente a *(*(mt+f)+c)
entonces se deduce que
&mt[f][c] es equivalente a *(mt+f)+c
Programas complementarios
Ejercicios propuestos
Realizar un programa que declare una variable entera var_a y un puntero a entero p_var_a.
Realizar un programa que lea de teclado 15 claves (números enteros) y las almacene en un
vector. Una clave será un número entero.
Posteriormente el programa deberá mostrar ordenadas las claves introducidas, pero sin
modificar el vector de claves. Para ello, se deberá utilizar un vector de punteros que apunten a
cada una de las claves.
También mostrará por último las claves en el orden original en que fueron introducidas, para
efectivamente comprobar que el vector original no se ha modificado.
Ayuda.
La forma de rellenar el vector de punteros es comprobar de una en una, comenzando por la
clave 1000 y hasta la 7000, si la clave se encuentra en el vector de claves, y si así fuera,
obtenemos la dirección del elemento del vector de claves y la incluimos en el primer puntero
libre del vector de punteros. Esto nos obliga a llevar un segundo índice sobre el vector de
punteros que nos vaya determinando el siguiente puntero libre.
Por último, recorreremos el vector de claves original y presentaremos cada una de sus claves.
Así, las claves aparecerán en pantalla en el orden original en que fueron introducidas.
A F H K L O S U X Z
El programa deberá disponer de una matriz interna de punteros a char, también de dimensión
3x4, que llamaremos mapa. En cada elemento de mapa se introducirá el siguiente valor:
− Cada vez que se teclee un carácter, se buscará en vletras, y si se encuentra, en el
elemento de mapa correspondiente a las coordenadas del carácter leído se
introducirá la dirección del elemento de vletras.
− En caso de no encontrase se introducirá NULL.
- una array bidimensional de int, que llamaremos tabla, de FIL x COL elementos (FIL = 3,
COL = 4) y con valores:
{ 15, 25, 35, 45,
115, 125, 135, 145,
215, 225, 235, 245}
3) Recorrido y presentación en pantalla utilizando una variable puntero al tipo base del array,
y aritmética de punteros. No se permite notación de subíndices.
Está claro que el resultado en pantalla de las anteriores tres implementaciones debe de ser el
mismo.
Está claro que el resultado en pantalla de las anteriores tres implementaciones debe de ser el
mismo.
Realizar un programa que solicite introducir por teclado una frase y posteriormente un
carácter.
Otros requisitos:
• El programa deberá ser lo más eficiente posible, de forma que la cadena sólo podrá
recorrerse una vez.
Este vector de punteros deberá haberse declarado de dimensión 64, que son como máximo el
número de submatrices 3x3 incluidas en una 10x10. Además deberá haberse iniciado al
principio con todos los punteros a NULL, para poder distinguir aquellos no usados.
NOTA: Todos los valores literales incluidos en el enunciado deberán declararse como
constantes en el programa, deduciendo los que sea posibles. Por ejemplo:
// Dimensiones matriz de entrada
#define FIL 10
#define COL FIL
// Dimensiones submatrices
#define SMFIL 3
#define SMCOL SMFIL
1 52 7 3 8 6 12 45 2 98
52 3 74 6 52 17 67 23 45 34
7 74 6 8 11 54 32 97 2 12
75 50 52 11 9 3 65 19 2 38
51 12 26 95 56 68 21 2 5 59
45 1 16 85 96 21 93 38 59 6
52 85 96 6 36 2 38 9 8 45
12 63 73 91 18 15 75 45 85 41
23 17 91 6 30 98 25 6 76 62
30 9 18 30 47 23 4 32 98 58
El programa solicitará por teclado la dimensión de matriz1 (sólo el número de filas, por
ejemplo, al ser cuadrada), y validará que no supera el máximo establecido. Si el valor tecleado
no es correcto deberá volverse a solicitarse repetidamente hasta que el usuario introduzca un
valor correcto.
Posteriormente solicitará uno tras otro los elementos de la matriz1, validando igualmente que
están dentro de los límites indicados. En cualquiera de las entradas, si se detecta un valor no
correcto, deberá volverse a solicitar repetidamente hasta que el usuario introduzca un valor
correcto.
Para la introducción de cada valor, es necesario indicar al usuario las coordenadas del
elemento que se está solicitando, con un mensaje del tipo
Introduzca el elemento [m,n]:
El valor de m y n, para el mensaje al usuario, deben de comenzar en [1,1].
El objetivo de programa es presentar en pantalla esta matriz rotada 90º hacia la derecha, pero
siguiendo exactamente el algoritmo que se detalla a continuación.
Internamente el programa utilizará una segunda matriz (matriz2) de punteros a enteros, y con
la misma dimensión física que matriz1.
El programa codificarás un algoritmo para rotar matriz1 sobre matriz2, pero en matriz2
deberá cargar no el valor del elemento de matriz1, sino el puntero a su posición.
Por último, el programa deberá presentar:
§ La matriz introducida, recorriendo matriz1 y presentando el valor de sus elementos.
§ La matriz rotada 90º hacia la derecha, recorriendo matriz2 y presentando el valor
apuntado por sus elementos, al ser punteros.