Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
tipo 2
tipo 3
Cada tipo de segmento pueden interpretarse como nmeros, caracteres, imgenes, etc. A veces un tipo de segmento tiene varias
interpretaciones, por ejemplo, un segmento que contenga 1000001 puede ser interpretado como 65 10 o como 4116 o como la letra A; para
hacer esto, solo escribimos :
int n = 65;
Memoria RAM
n
...01011 1000001 01100..
0x7ff...c (direccin de n en base hexadecimal)
En la realidad solo existen los nmeros 0 y 1; las indicaciones en rojo sirven para darnos informacin que nos ayuda a comprender
En tiempo de ejecucin, el sistema operativo, ubica a n en una posicin disponible, por ejemplo, 0x7ff...c. De corrida en corrida, es casi
seguro que esta posicin cambie. El programador no maneja esas posiciones, hasta ahora no ha sido importante; pero pronto veremos
aplicaciones muy importantes del manejo (indirecto) de las posiciones.
Preparemos las herramientas: Ya hemos utilizado en modo limitado dos operadores:
Operador &: suministra la direccin de una variable, por ejemplo:
printf(%p\n, &n);
Salida: 0x7ff...c
Operador *: suministra el contenido de una direccin, por ejemplo:
printf(%d\n, *&n);
Salida: 65
En resumen:
Operador Descripcin
&
Operador de direccin (referencia)
: &n = direccin de n = 0x7ff...c
*
Operador de indireccin (dereferencia): *&n = valor de la direccin de n = 65
* es casi el inverso de &: *&n = *(&n) = n; pero &*n no compila.
Apuntador
Un apuntador ptr es una variable, como cualquier otra, diseada para guardar la direccin de otra variable, ejemplo, dada una variable:
int n = 65;
Podemos guardar en ptr la posicin de n:
ptr = &n;
Como de costumbre, podramos definir y asignar valor a la variable ptr as:
puntero ptr = &n;
Para mayor precisin, debemos indicar que ptr guarda direcciones de variables de tipo int; entonces podramos precisar:
int puntero ptr = &n;
Lo mismo sucedera para otros tipos de variables, ejemplo:
float datoFloat;
float puntero ptrf = &datoFloat;
PGINA: 1
Lenguaje C
Resulta exagerado duplicar el nmero de tipos de variables, por culpa de los apuntadores; veamos un artificio que nos da una solucin
corta y elegante. Tenemos una definicin:
int n;
int puntero ptr = &n;
Si aplicamos el operador * a ptr, podemos escribir:
int n;
int *ptr = &n;
// *ptr es el valor apuntado por ptr
Se lee as:
El valor apuntado por ptr es de tipo int
int *ptr = &n;
valor de ptr = direccin de n
Tambin se puede definir y asignar por separado en dos instrucciones:
int n = 65;
int *ptr;
ptr = &n;
// ptr toma como valor la direccin de n
O definir todo junto:
int n = 65, *ptr = &n;
Se representa as:
memoria
ptr
0x7ff...c
0x7ff...0
n
65
0x7ff...c
Lenguaje C
5) Variables y valores del ejemplo:
n = 65
ptr = 0x7ff...c
&n = 0x7ff...c
&ptr = 0x7ff...0
*n error, n no es posicin de memoria *ptr = 65
6) Mientras ptr apunte a n, se cumplen las siguientes relaciones:
Variables
Relacin
Ejemplo
&n y ptr
n y *ptr
Equivalentes
Leer valor de n:
scanf(%d, &n);
scanf(%d, ptr);
No equivalentes
ptr = &m;
&n = algo;
Equivalentes
Escribir valor de n
printf(%d, n);
printf(%d, *ptr);
p2
0x7ff...a
n
2
0x7ff...a
m
8
Observaciones:
El valor de n puede cambiar de 3 modos:
n = 4;
*p1 = 4;
*p2 = 4;
Un puntero apunta a 0 o 1 variable; pero puede cambiar de variable apuntada:
p1 = &m;
// y deja de apuntar a n
Apuntador constante
int a=10, b=20;
int * const p = &a; // p apunta solo a la variable a.
*p = 15;
// Correcto: asigna 15 a a, el valor apuntado es variable.
p=&b;
// ERROR: El valor de p es constante
p
a
0x7ff...a
15
0x7ff...a
b
20
PGINA: 3
Lenguaje C
Ya utilizamos apuntadores en funciones para pasar valores por referencia:
miFun(&n);
// el valor de n cambiar a 3.
Llama a:
void miFun(int *n) {*n= 3;}
Pero el uso ms frecuente es para apuntar a bloques de datos, como veremos a continuacin.
Valor
arr;
0x7ff...a
arr[0];
*(arr+0); *arr;
10
arr[1];
*(arr+1);
20
arr[2];
*(arr+2);
30
arr[1] = 80;
*(arr+1) = 80;
80
n = fun(arr, 3);
n = fun(arr, 3);
// Notacin de apuntadores
PGINA: 4
Lenguaje C
ptr
0x7ff...0
0x7fa...b
arr
10 20
0x7ff...0
arr1
30
Aritmtica de punteros
Dada la naturaleza de un puntero -apunta a una direccin sobre una lnea-, el puntero se puede desplazar a la derecha-izquierda,
mediante sumas y restas:
ptr = ptr + 1;
// ptr apunta a la siguiente posicin de tipo int: avanza 4 bytes: 0x7ff...4
// si ptr apuntara a un tipo long, sumar 8 bytes, para char sumar 1 byte; etc.
ptr = ptr + n;
// equivalente a ptr += n;
ptr = ptr - n;
// equivalente a ptr -= n;
ptr + 2*3+1;
// = pptr + 7; apunta 7 posiciones de bytes, es decir 7*4 bytes ms adelante.
Se permite la resta de dos punteros:
int nn[10], *ptr1, *ptr2, n;
ptr1 = nn;
// equivalente a: ptr1 = &nn[0];
// apunta a nn[0].
ptr2 = &nn[2];
// apunta a nn[2].
n = ptr2 ptr1;
// diferencia en posiciones enteras = 2.
No se permiten otro tipo de operaciones
ptr = ptr * 2;
// Error: No se admiten multiplicaciones de valores de memoria
ptr1 = ptr1 + ptr2;
// Error
Mientras ptr apunte a arr:
int arr[3] = {10, 20, 30}, *ptr = arr;
Son equivalentes (sinnimos) por completo
arr[i] = = ptr[i];
*(arr+i) = = *(ptr+i);
Pre y postoperadores, funcionan como siempre:
arr++;
// error porque arr es puntero constante
ptr++;
// aumenta 1 a ptr;
printf("%p\n", ptr++);
// printf("%p\n", ptr); ptr++;
printf("%p\n", (ptr+1)++);
// error de compilacin, no se puede postoperar a una expresin (ptr+1)
*arr++;
*ptr++;
printf("%d\n", *ptr++);
printf("%d\n",*(ptr++));
printf("%d\n", *(ptr+1)++);
++arr;
++ptr;
printf("%p\n", ++ptr);
printf("%p\n", ++(ptr+1));
*arr++;
++*ptr;
printf("%d\n", ++*ptr);
printf("%d\n",++(*ptr));
printf("%d\n", ++*(ptr+1));
PGINA: 5
Lenguaje C
arr[0][0]
arr[0][1]
arr[0][2]
arr[0][3]
arr[1][0]
arr[1][1]
arr[1][2] arr[1][3]
Valores
2
4
6
7
8
10
12
0X7234... = posicin fsica
Analizando las posiciones lineales y matriciales anteriores, deducimos que:
arr[i][j] ocupa la posicin i*4+j en la memoria
En general. para un arreglo arr[m][n], el elemento arr[i][j] ocupa la posicin: i*n+j en la memoria.
14
Los arreglos de dos dimensiones nos ayudan pero presentan tres limitaciones:
El lenguaje C los concibe como un arreglo de arreglos
arr[0]: fila 0
arr[1]: fila 1
// arr[0][0] 2
// arr[1][0] 8
arr[0][1] 4
arr[1][1] 10
arr[0][2] 6
arr[1][2] 12
arr[0][3] 7
arr[1][3] 14
Se debe estimar el nmero de fila y columnas por exceso, lo cual es un gran desperdicio de memoria, por otra parte si nos
excedemos, no hay advertencia de ello, y nos esperan errores y/o el aborto del programa.
Afortunadamente, se resuelven los tres problemas anteriores en modo ptimo: Trabajar con punteros, asignar memoria dinmicamente y
controlar el nmero de elementos en cada dimensin; lo cual haremos en dos captulos ms adelante; por el momento hagamos una
aproximacin conceptual trabajando con arreglos y apuntadores:
0
arr[0][0]
2
0x7234...
2
arr[0][2]
6
3
arr[1][0]
8
4
arr[1][1]
10
5
arr[1][2]
12
#include <stdio.h>
void mitad(int *n);
// prototipo
void main(void){
int n = 2;
mitad(&n);
// llamando
printf("n = %d\n", n);
#include <stdio.h>
void mitad(int *pn); // prototipo
void main(void){
int n = 2, *pn = &n;
mitad(pn);
// llamando
printf("n = %d\n", n);
PGINA: 6
Lenguaje C
}
void mitad(int n){
n /=2;
}
}
void mitad(int *n){
*n /=2;
}
// funcin
Salida: n = 2
// funcin
Salida: n = 1;
}
void mitad(int *pn){
*pn /=2;
}
// funcin
Salida: n = 1;
1 dimensin
2 dimensiones
#include <stdio.h>
void mitad(int *parr, int n);
void main(void){
int arr[4] = {1, 2, 3, 4}, *parr = arr, i;
mitad(parr, 4);
for(i=0; i < 4; i++)
printf("%d\t", arr[i]);
}
void mitad(int *parr, int n){
int i;
for(i=0; i < n; i++) *(parr+i) /=2;
}
01
02
03
04
05
06
07
08
09
10
11
12
13
#include <stdio.h>
void mitad(int *parr, int m, int n);
void main(void){
int arr[2][2] = {1, 2, 3, 4}, *parr = arr, i, j;
mitad(parr, 2, 2);
for(i=0; i < 2; i++)
for(j=0; j < 2; j++) printf("%d\t", arr[i][j]);
}
void mitad(int *parr, int m, int n){
int i, j;
for(i=0; i < m; i++)
for(j=0; j < n; j++) *(parr+i*n+j) /=2;
}
#include <stdio.h>
void mitad(int parr[][2], int m, int n);
void main(void){
int arr[2][2] = {1, 2, 3, 4}, i, j;
mitad(arr, 2, 2);
for(i=0; i < 2; i++)
for(j=0; j < 2; j++) printf("%d\t", arr[i][j]);
}
void mitad(int arr[][2], int m, int n){
int i, j;
for(i=0; i < m; i++)
for(j=0; j < n; j++) arr[i][j] /=2;
}
Salida: 0 1 1 2
Salida: 0 1 1 2
Salida: 0 1 1 2
Nota: 1
Nota: 2
Nota: 3
Notas:
1: El programa es paramtrico en una dimensin.
2: El programa es paramtrico en las dos dimensiones; pero compila con un warning (advertencia) por tipo de apuntador en la lnea 04,
debido a que un apuntador de tipo int (lineal) apunta a una matriz (arreglo de arreglos) de tipo int; pero ejecuta bien (por que
controlamos las dimensiones). El programa compilar limpiamente cuando usemos solo punteros y memoria dinmica, que ser dos
captulos ms adelante.
3: El programa es paramtrico solo en la primera dimensin, ms no en la segunda.
Ejercicio: Definir un arreglo, calcular el mnimo, la suma y la media
Veamos dos temas que usaremos ms adelante.
Arreglo de apuntadores
Un puntero es una variable como cualquier otra, por lo tanto podemos tener arreglos de punteros, ejemplo:
int *ptr[4], n = 6;
ptr[1] = &n;
*ptr[1];
// = 6
ptr
Posiciones
0xff..c
1
2
n
6
0xff..c
Apuntador a apuntador
Un puntero es una variable como cualquier otra, por lo tanto podemos definir un puntero a puntero:
Sintaxis: tipo **nombreApuntador, ejemplo:
int a = 3, *b = &a, **c = &b;
c
0x7234..
0x65734..
printf("%d\n", a);
printf("%d\n", *b);
b
0x8174..
0x7234..
a
3
0x8174..
// resultado: 3
// resultado: 3
PGINA: 7
Lenguaje C
printf("%d\n", **c);
a = 4;
// resultado: 3
// es equivalente a: *b = 4; **c = 4;
arr
15
0x8174..
13
11
m
2
n
6
Programa 06_03.c:
#include<stdio.h>
void main(void){
int *ptr[2][3], j= 2, m=6, n=4;
ptr[0][0] = &j;
ptr[0][1] = &m;
ptr[0][2] = &n;
printf("%d\n", *ptr[0][0]);
printf("%d\n", *ptr[0][1]);
printf("%d\n", *ptr[0][2]);
}
Salida:
2
6
4
ejemplo:
int (*ptr)[2][3];
(*ptr)
Es un puntero
(*ptr)[2][3] Es un puntero a arreglos [2][3] de tipo int
PGINA: 8
Lenguaje C
Ejemplo:
int aa[2][3] = {1,2,3,4,5,6};
int *ptr = aa;
ptr
aa
1
Apuntador a void
La palabra reservada void (se pronuncia void en ingls y significa vaco, nulo, vacante, ...) se utiliza para crear un puntero sin especificar
el tipo de dato al que apuntar:
int
in=1;
double db=2.4;
void *pvoid; // un apuntador a void puede apuntar a cualquier tipo de dato:
pvoid = ∈
pvoid = &db;
pin
int
*pin = (int *)pvoid;
double *pdb = (double *)pvoid;
pdb
pvoid
printf("%d\n", *pin);
printf("%lf\n", *pdb);
in
// = 1
// = 2.4
db
1
2.4
Ejemplo: Dado un puntero como, float *fp. Verifique que al sumar n a fp (fp += n); la direccin apuntada por fp aumenta a:
n*sizeof(float).
Programa 06_04.c:
#include<stdio.h>
void main(void){
float f, *fp = &f;
long int d1, d2;
int n=2;
printf("\nPrimera direccin apuntada: %p (en hexadecimal)\n", fp);
d1 = (long int)fp;
// primera direccin apuntada
fp +=n;
d2 = (long int)fp;
// segunda direccin apuntada
printf("Segunda direccin apuntada: %p (fp + 2)\n", fp);
printf("
--------------\n");
printf("Aumento de direccin : %lu\n", d2 - d1);
printf("n*sizeof(float) = %d * %lu = : %lu\n", n, sizeof(float), n*sizeof(float));
}
Salida:
Primera direccin apuntada : 0x7fff329c5ee0 (en hexadecimal)
Segunda direccin apuntada: 0x7fff329c5ee8 (fp + 2)
------------------Aumento de direccin
:
8
PGINA: 9
Lenguaje C
n*sizeof(float) = 2 * 4 =
Ejercicios:
1) Para un arreglo tridimensional, por ejemplo:
int a[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} ;
Que posicin lineal ocupar a[i][j][k]?
2) Una matriz p[4][12] representa una produccin de los 12 meses en 4 aos. Lea la matriz y use punteros para calcular el promedio de
produccin en cada ao y en los 12 meses:
Aos
1
2
3
4
Promedio:
xx
xx
xx
xx
Promedio:
Meses
1
2
xx
xx
3
xx
4
xx
12
xx
PGINA: 10