Académique Documents
Professionnel Documents
Culture Documents
Temas
● Punteros o apuntador
● Declaración de punteros
● Operadores dirección (&) e indirección (*)
● Aritmética de punteros
● Punteros y arreglos
● Funciones de asignación dinámica, malloc() y free()
● Punteros o apuntador
Se conoce como puntero a una variable que contiene una dirección de memoria.
Normalmente, esa dirección es la posición de otra variable de memoria. Si una variable
contiene la dirección de otra variable, entonces se dice que la primera variable apunta a
la segunda.
Al una variable contener un puntero, se debe declarar como tal. Una declaración
de un puntero consiste en un tipo base, un * y el nombre de la variable. La forma
general es:tipo *nombre; donde tipo es cualquier tipo válido y nombre es el nombre de
la variable puntero. El tipo base del puntero define el tipo de variables a las que puede
apuntar.
Desde el punto de vista técnico, cualquier tipo de puntero puede apuntar a
cualquier dirección de la memoria, sin embargo, toda la aritmética de punteros se lleva
acabo en relación a sus tipos base, por lo que es importante declarar correctamente el
puntero.
Se cuenta con dos operadores especiales de punteros: & y *. El operador de
dirección (&) devuelve la dirección de memoria de su operando. El operador de
indirección (*) devuelve el contenido de la dirección apuntada por el operando.
Una vez se declara un puntero, pero antes de asignarle un valor, éste contiene un
valor desconocido; si en ese instante lo intenta utilizar probablemente no funcionará, no
sólo el programa sino también el sistema operativo. Se debe asignar por convenio el
valor nulo a un puntero que no se encuentre apuntando a ningún sitio, aunque ésto
tampoco es seguro.
● Declaración de punteros
Asignación de punteros
int x;
int *p1,*p2;
p1=&x;
p2=p1;
Tanto p1 como p2 apuntan a x.
Como las constantes y las expresiones no tienen dirección, no se les puede aplicar
el operador (&), tampoco se puede cambiar la dirección de una variable. Los valores
posibles para un puntero son las direcciones posibles de memoria. Un puntero puede
tener valor 0, la cual es equivalente a la constante simbólica predefinida NULL. No se
puede asignar una dirección absoluta de forma directa, se debe hacer un casting. Las
siguientes sentencias son ilegales:
Para imprimir punteros con la función printf() se deben utilizar los formatos %u y
%p.
No se puede realizar asignaciones directas (sin casting) entre punteros que apuntan
a distintos tipos de variables. Sin embargo, existe un tipo indefinido de punteros (void *,
o punteros a void), los cuales pueden ser asignados, y a los que puede asignárseles
cualquier tipo de puntero. Por ejemplo:
int *p;
double *q;
void *r;
p = q; // ilegal
p = (int *)q; // legal
p = r = q; // legal
● Aritmética de punteros
De auerdo con lo visto, los punteros son unas variables especiales, ya que guardan
información, no sólo sobre la dirección a la que apuntan, sino también del tipo de
variable almacenado en dicha dirección. Esto implica que no se permitirán las
operaciones que no tienen sentido con direcciones de variables, como multiplicar o
dividir, pero sí otras como sumar o restar. Además, estas operaciones se realizan de un
modo correcto, pero que no es el ordinario. Así, la sentencia:
p = p+1;
hace que p apunte a la dirección siguiente de la que apuntaba, teniendo en cuenta el tipo
de dato. Por ejemplo, si el valor apuntado por p es short int y ocupa 2 bytes, el sumar 1
a p implica añadir 2 bytes a la dirección que contiene, mientras que si p apunta a un
double, sumarle 1 implica añadirle 8 bytes.
p = p + 1;
p = p + i;
p += 1;
p++;
Pág. 6 Curso: Lenguaje C Unidad 4: Tipo de datos
derivados
Programa de Formación de la Academia de Software Libre
Para resumir, sólo dos operaciones aritméticas pueden ser identificadas, las cuales
pueden utilizarse con punteros: la suma y la resta.
Por ejemplo, se asumirá que los enteros son de dos bytes de longitud y p1 es un
puntero a entero con valor actual 2000. En consecuencia, después de la expresión p1+
+; p1 contiene el valor 2002, no 2001.
No se pueden realizar otras operaciones aritméticas sobre los punteros más allá de
la suma y resta de un puntero y un entero. En particular, no se pueden multiplicar o
dividir punteros y no se puede sumar o restar el tipo float o el tipo double a los punteros.
El siguiente ejemplo ilustra la aritmética de punteros, la tabla 4.1 muestra las
direcciones de memoria asignadas a las variables del ejemplo:
void main(void) {
int a, b, c;
int *p1, *p2;
void *p;
p1 = &a; // Paso 1. La dirección de a es asignada a p1
*p1 = 1; // Paso 2. p1 (a) es igual a 1. Equivale a a = 1;
p2 = &b; // Paso 3. La dirección de b es asignada a p2
Pág. 6 Curso: Lenguaje C Unidad 4: Tipo de datos
derivados
Programa de Formación de la Academia de Software Libre
Paso a b c p1 p2 p
00FA:0000 00FA:0002 00FA:0004 00FA:0006 00FA:000A 00FA:000E
1 00FA:0000
2 1 00FA:0000
3 1 00FA:0000 00FA:0002
4 1 00FA:0000 00FA:0002
5 1 2 00FA:0002 00FA:0002
6 1 2 00FA:0002 00FA:0002
7 1 0 00FA:0002 00FA:0004
8 1 0 3 00FA:0002 00FA:0004
9 1 0 3 00FA:0002 00FA:0004
10 1 0 3 00FA:0002 00FA:0004 00FA:0006
11 1 0 3 00FA:0004 00FA:0004 00FA:0006
12 1 0 1 00FA:0004 00FA:0004 00FA:0006
● Punteros y arraglos
Existe una estrecha relación entre los punteros y los arreglos. Tómese en cuenta el
siguiente fragmento:
En éste, p1 ha sido asignado a la dirección del primer elemento del arreglo cad.
Para acceder al quinto elemento de cad se escribe cad[4] o *(p1+4).
Se devolverá la dirección de comienzo del arreglo, que es el primer elemento, si
aparece un nombre de arreglo sin índice. El compilador traduce la notación de arreglos a
notación de punteros.
Esto es igual a decir, que al crear un arreglo se genera un puntero, una constante
de puntero en realidad, con el mismo nombre que apunta a la dirección del primer
elemento del arreglo.
Los punteros pueden ser estructurados en arreglos como cualquier otro tipo de
datos. La declaración, por ejemplo, para un arreglo de punteros a enteros de tamaño 10
es:
int *x[10];
Para asignar la dirección de una variable entera llamada var al tercer elemento del
arreglo de punteros se escribe:
x[2]=&var;
Para ilustrar con un ejemplo, se desea que el usuario introduzca varios números
de teléfono y se almacenen en el arreglo teléfonos. Se desconoce cuántos números va a
introducir el usuario. Si se tuviese que implementar con un arreglo definido desde su
declaración, se tendría que asignar un tope a los números de teléfono que se pueden
introducir, por ejemplo 10:
int i;
int telefonos[10];
i=0;
while (usuario_introduce && (i<10))
{
telefonos[i] = numero_introducido;
i++;
}
numeros_introducidos = i;
int i, tamano;
int *telefonos; // o bien: int telefonos[]
telefonos = NULL;
/* es muy recomendable inicializar el valor de un puntero a NULL.
* Así, si se nos olvida pedir memoria para él, el programa fallará siempre.
* Por el contrario, si no se inicializa, el programa fallará
* algunas veces sí y otras no. */
tamano = preguntar_tamano();
telefonos = malloc (tamano);
for (i=0;i<tamano;i++)
telefonos[i] = numero_introducido;
telefonos = NULL;
i=0;
while (usuario_introduce)
{
nuevo_tamano = sizeof (int) * (i+1);
telefonos = realloc (telefonos,nuevo_tamano);
telefonos[i] = numero_introducido;
i++;
}
tamano=i;
char *p;
p = (char *) malloc(1000);
Nótese el uso de sizeof para asegurar la portabilidad: p apunta al primero de los 1000
bytes de la memoria libre. El siguiente ejemplo dispone espacio para 50 enteros. Nótese
también el uso de sizeof para asegurar la portabilidad:
int *p;
p= (int *) malloc(50*sizeof(int));