Está en la página 1de 36

Captulo 8

Apuntadores
Direcciones de Memoria
Podemos imaginarnos la memoria principal de una computadora como un conjunto de
celdas consecutivas de memoria cada una de un tamao de un byte. Cada celda tiene su
propia direccin, como se ilustra en la figura 8-1.

Figura 8-1. Memoria Principal


Una variable aparte de tener un nombre tiene tambin una direccin. Esta direccin es
nica. Si la variable es de un byte su direccin es la direccin de la celda que la contiene.
Si una variable ocupa dos o ms bytes, su direccin es la direccin de su primera celda,
esto es la direccin ms baja. En la figura 8-2 se muestra el mismo bloque de memoria de
la figura 8-1 despus de que se han hecho las siguientes declaraciones, suponiendo que
una variable de tipo int ocupa 4 bytes de memoria:
char x = 8;
int y = 1234;

ITSON

Manuel Domitsu Kono

124

Apuntadores

Figura 8-2. Memoria principal con variables


Dado que cada variable tiene una direccin nica, es posible referirse a una variable ya
sea por su nombre o por su direccin. Por ejemplo podramos referirnos a la variable x
declarada anteriormente, como a la variable de tipo char que tiene la direccin 1000 y a y
como a la variable entera cuya direccin es 1001. De hecho, en el lenguaje mquina todas
las referencias a las variables son a travs de sus direcciones ya que no existen los
nombres de variables. Uno de los grandes avances introducidos por los lenguajes de
programacin es el concepto de los nombres simblicos de las variables tal como x, y. Lo
que nos libera de recordar las direcciones en las que se encuentran las variables.
Una de las caractersticas con las que fue diseado el lenguaje C es la permitirnos un
control casi completo del hardware al mismo tiempo de que la programacin fuese al
mismo nivel que con un lenguaje de alto nivel. Para lograr este control del hardware, as
como para hacer ms eficientes algunas de las tareas para acceder las variables, C
requiere de la capacidad de manipular directamente las direcciones de memoria.

Operador de Direccin (&)


En realidad, desde los primeros programas hemos usado el concepto de direccin de una
variable. Recuerde que en la funcin scanf(), le pasamos como argumentos las
direcciones de las variables donde queremos que se almacenen los datos ledos. Para
determinar la direccin en que se encuentra una variable usamos el operador de
direccin (&), el cual es un operador unario que nos da la direccin de una variable. El
operador & slo opera con variables creadas en la memoria no con expresiones ni
variables registro. Para las declaraciones de variables del ejemplo anterior, la expresin
&x vale 1000 y &y vale 1001. El operador de direccin aparece en la tabla de
precedencias en la segunda lnea.

ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

125

Apuntadores
Las direcciones de las variables son constantes, esto es, no cambian de posicin durante
la ejecucin del programa. Sin embargo la necesidad de crear expresiones para calcular
de direccin de un dato o de escribir instrucciones que operen con diferentes direcciones
introduce la necesidad de tener variables que en lugar de almacenar datos comunes,
almacenen direcciones. A tales variables se le conocen como apuntadores.
Al igual que con las variables comunes los apuntadores deben declararse antes de
usarse. La sintaxis para declarar un apuntador es la siguiente:
tipo *nomPvar1[ = pExp1][, *nomPvar2[ = pExp2]...]

nomPvar1, nomPvar2 ... son los apuntadores que se estn declarando. El asterisco al
lado de cada identificador indica que es una declaracin de un apuntador y no de una
variable comn. El apuntador va a contener una direccin de un dato del tipo tipo
especificado en la declaracin. Se dice que el apuntador apunta al dato del tipo
especificado.
Al igual que con las variables comunes, los apuntadores pueden inicializarse al momento
de su declaracin. pExp1, pExp2, ... son expresiones que al evaluarse nos dan las
direcciones a las que se inicializan los apuntadores nomPvar1, nomPvar2 ...,
respectivamente.
Por ejemplo:
char *px;
int *py;

Aqu estamos declarando a px como un apuntador a carcter y a py como un apuntador a


entero. Esto es, px va a contener la direccin de una variable de tipo carcter y py la de
una variable de tipo entero.
Antes de poder usar un apuntador, ste debe apuntar a una variable, esto es, debemos
asignarle la direccin de una variable. Esto se puede hacer inicializando el apuntador al
declararlo o mediante una asignacin posterior. Por ejemplo.
char x = 8;
int y = 1234;
char *px = &x;
int *py;
...
py = &y;

Despus de efectuarse las asignaciones anteriores, px apunta a x, esto es, contiene la


direccin de x y py apunta a y. Esto se ilustra en la figura 8-3.

ITSON

Manuel Domitsu Kono

126

Apuntadores

Figura 8-3.

Inicializacin de apuntadores por ausencia


Los conceptos de clase de almacenamiento y alcance lxico se aplican de igual forma a
los apuntadores que a las variables normales. Los apuntadores con clase de
almacenamiento automtica si no son inicializados tienen una direccin indefinida llamada
direccin basura. Si son inicializados, el inicializador puede ser cualquier expresin que
pueda ser evaluada al tiempo de la inicializacin y que produzca una direccin vlida. La
inicializacin se hace cada vez que se entra a la funcin o bloque.
Si los apuntadores son de clase de almacenamiento esttica y no son inicializados, el
compilador los inicializa a una direccin especial llamada direccin nula. En el caso de
ser inicializadas explcitamente, los apuntadores con clase de almacenamiento esttica
declarados fuera de las funciones (externos) deben inicializarse a una direccin constante,
o la direccin de una variable externa. Los apuntadores con clase de almacenamiento
esttica declarados en un bloque (estticos) deben inicializarse a una direccin constante,
o la direccin de una variable externa o esttica en la misma funcin. La inicializacin se
realiza una vez, antes de que el programa inicialice su ejecucin.

Direccin Basura
Una direccin basura es una direccin al azar. Un apuntador con una direccin basura
puede apuntar a una de estas tres direcciones: La direccin de una variable del programa,
la direccin donde est una instruccin del programa o a una direccin que no le
pertenece al programa. En el primer caso corromperamos el contenido de una variable,
en el segundo caso estaramos modificando una instruccin del programa y en el tercer
caso, dependiendo de la proteccin de memoria que ofrezca el sistema operativo,
pudiramos corromper los datos o cdigo de otro programa o inclusive del sistema
operativo. Como resultado puede suceder que el programa se comporte en forma errnea
o deje de funcionar.

ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

127

Direccin Nula
Una direccin nula representa una direccin especial que la definicin del lenguaje C,
asegura que no pertenece a ningn objeto o funcin a la que pueda hacer referencia el
programa. Como esta direccin no le pertenece a ningn programa, si escribimos en esta
direccin no corromperemos a ningn dato o instruccin. Si no vamos a inicializar a un
apuntador al momento de declararlo, sino hasta ms adelante en el cdigo, es una buena
prctica de programacin inicializarlo a esa direccin nula para evitar que
inadvertidamente usemos el apuntador y escribamos en cualquier direccin.

Apuntador Nulo
En los datos numricos, el cero denota la ausencia de lo que representa el dato. De la
misma manera, en el lenguaje C, un apuntador nulo representa una direccin que no
existe, es decir, la definicin del lenguaje C asegura que un apuntador nulo es una
direccin "especial" diferente a la direccin de cualquier objeto o funcin a la que pueda
hacer referencia el programa. Si inicializamos un apuntador a un apuntador nulo queremos
decir que ese apuntador no apunta a lado alguno.
Hay que hacer la distincin entre un apuntador nulo y un apuntador no inicializado. Un
apuntador nulo no apunta a algn lado mientras que un apuntador no inicializado puede
apuntar a cualquier lado.
Un apuntador nulo se representa por la constante cero (0). Cuando el compilador ve la
constante cero en el contexto de un apuntador la convierte a un apuntador nulo. Por
ejemplo en la siguiente declaracin px se est inicializando a un apuntador nulo:
int px = 0;

mientras que en la siguiente expresin estamos comparando el valor de px con un


apuntador nulo
px == 0

Sin embargo si uno de los argumentos de una llamada a una funcin es un apuntador y no
existe el prototipo de la funcin, el compilador puede fallar en interpretar el cero como
apuntador nulo. En estos casos se debe aplicar el operador cast para convertir el 0 a un
apuntador nulo del tipo esperado por la funcin. Por ejemplo supongamos que la funcin
f1() tiene un apuntador a char como parmetro, y deseamos llamar a la funcin
pasndole un apuntador nulo, la llamada a la funcin debe escribirse como
f1( (char *)0 )

Si existe el prototipo de la funcin, el compilador podr realizar la conversin


correctamente y no ser necesario el operador cast.

ITSON

Manuel Domitsu Kono

128

Apuntadores

El lenguaje C define la macro


#define NULL 0

en los archivos de encabezados stdio.h y stddef.h. Podemos usar 0 o NULL


indistintamente en el contexto de apuntadores. No debemos usar NULL para indicar una
constante numrica que valga cero.

Operador de indireccin
Suponga que se han hecho las siguientes declaraciones:
int x = 8, *px = &x;

y que posteriormente deseamos acceder el contenido de la variable x para cambiarlo a 5.


Una de las formas es a travs de su nombre
x = 5

La segunda forma es utilizar el apuntador a esa variable, que es px


*px = 5

en la expresin anterior, el asterisco se conoce como el operador de indireccin o de


desreferenciacin. Este operador unario nos permite acceder a la variable cuya direccin
est dada por su operando. El operando del operador de indireccin es una expresin que
produce una direccin. En este ejemplo, modificamos el valor de la variable x
indirectamente a travs de la variable px. Este concepto se conoce como
direccionamiento indirecto o simplemente como indireccin. La precedencia y
asociatividad del operador de indireccin son las mismas que las del operador de
direccin.
La expresin *px del ejemplo anterior es equivalente a x, esto es, dado que x puede
aparecer en cualquier expresin donde aparezca un entero, *px tambin lo puede hacer.
Por ejemplo
x += 5;

se puede escribir como


*px += 5;

Sin embargo, la expresin


x++

debe escribirse como

ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

129

(*px)++

Los parntesis son importantes pues lo que queremos es incrementar el valor apuntado
por px. Si omitimos los parntesis
*px++

dada la asociatividad de los operadores (ya que tienen la misma precedencia), equivale a
*(px++)

y lo que estaramos aumentando es el valor de px y no el valor al que apunta.


Un aspecto importante es el hecho de que un apuntador apunta a una variable de un tipo
dado. Ya vimos que en la declaracin
char x = 8, *px = &x;
int y = 1234, *py;

la asignacin px = &x es correcta. Sin embargo la asignacin


px = &y

genera un error del compilador, pues px es una variable que apunta a una variable
carcter y no a una variable entero.

Apuntadores y el calificador const


En el Captulo 3: Tipos de Datos y Expresiones en C vimos que el calificador const puede
aplicarse a la declaracin de cualquier variable para especificar que su valor no cambia,
comportndose como una constante. Por consiguiente, no podemos obtener la direccin
de esa variable ya que de hacerlo pudiramos asignrselo a un apuntador y usar ste
para acceder indirectamente a la variable y modificarla. Por ejemplo
const int x = 8;
int *px = &x;

// Error de compilacin

As como podemos calificar a una variable como const, podemos hacer lo mismo con un
apuntador. Por ejemplo
int x = 5, y = 6;
int *const px = &x;

En esta segunda declaracin decimos que px no puede cambiar su valor. Sin embargo el
valor apuntado por px si puede cambiar
*px = 7;

ITSON

// Valida

Manuel Domitsu Kono

130

Apuntadores
px = &y;

// Error de compilacin

Tambin podemos permitir que el apuntador cambie de valor pero la variable apuntada por
el apuntador no. Por ejemplo
int x = 5, y = 6;
const int *px = &x;

En esta segunda declaracin decimos que px puede cambiar su valor. Sin embargo el
valor apuntado por px no puede cambiar
px = &y;
*px = 7;

// Valida
// Error de compilacin

Por ltimo podemos impedir que tanto el apuntador y la variable a la que apunte cambien
de valor. Por ejemplo
int x = 5, y = 6;
const int const *px = &x;

En esta segunda declaracin decimos que ni px ni el valor apuntado por px pueden


cambiar.
*px = 7;
px = &y;

// Error de compilacin
// Error de compilacin

Apuntadores como Parmetros de


Funciones
Una de las limitantes que tienen las funciones en C, es de que slo pueden regresar un
valor por el mecanismo de return. Cuando deseamos que una funcin "regrese" ms de
un valor podemos recurrir al uso de variables externas tal como se plante en el ejemplo 1
del Captulo 6: Funciones. Sin embargo esta solucin va en contra de la idea de que la
comunicacin entre funciones debe ser a travs de parmetros y del mecanismo de return
ya que una variable externa es conocida por todo el programa y debemos tener ms
cuidado para no alterarla inadvertidamente.
Una segunda alternativa para resolver este problema es que la funcin llamante declare
las variables que requiere para recibir los datos y le pase a la funcin llamada las
direcciones de las variables. Con esto le permite el acceso a las variables. La funcin
llamada puede modificar directamente las variables en la funcin llamante simulando as
que le regresan valores. El mecanismo anterior se ilustra en la figura 8-4.
Podemos ver que la funcin f0() declara dos variables x e y. Al llamar a la funcin f1()
la funcin f0() le pasa las direcciones de las variables, &x y &y.

ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

131

Figura 8-4
void f0(void) {
int x, y;
...
f1(&x, &y);
...
}

La funcin f1() recibe estas direcciones en sus parmetros tipo apuntador, px y py,
obteniendo as el acceso a las variables x e y de f0().
void f1(int *px, int *py) {
...
}

El uso de apuntadores como parmetros de una funcin no slo nos permite que la
funcin "regrese" ms de un valor. Dado a que la funcin tiene acceso a las variables
cuyas direcciones constituyen sus parmetros, la funcin puede tomar los valores que ah
se encuentran. Esto es, los parmetros apuntadores permiten que la funcin no slo
"regrese" valores sino que tambin los "reciba".
El mecanismo explicado anteriormente, para simular que una funcin nos regresa ms de
un valor se usa ampliamente en C. De hecho una funcin que hemos usado desde
nuestro primer programa lo emplea, la funcin scanf(). Recuerde que la funcin
scanf() requiere que le pasemos las direcciones de las variables en las que va a
almacenar los datos ledos. Al darle las direcciones de las variables le damos el acceso a
dichas variables.
Dado que es vlido cambiar el contenido de un parmetro de una funcin, en el ejemplo
anterior podramos inadvertidamente modificar el valor de los parmetros apuntadores,
esto traera como consecuencia que estos ya no apuntaran a las variables a las que se
desea "regresar" los valores. Para evitar esa posibilidad podemos declarar los parmetros
de la siguiente forma:
void f1(int *const px, int *const py) {
...
}

ITSON

Manuel Domitsu Kono

132

Apuntadores

As cualquier intento por modificar los valores de px o py generaran un error del


compilador.

Ejemplo sobre Apuntadores como Parmetros de Funciones


Modifique el programa para el clculo del rea bajo una curva del ejemplo 1 del Captulo 6:
Funciones, para hacer que la funcin usada para leer los datos en lugar de usar variables
globales para las abcisas, las "regrese" por el mecanismo de parmetros apuntador.
Recordemos que la funcin leeDatos() lee del teclado la abscisa inicial, xi, la abscisa
final, xf y el nmero de rectngulos, n. La funcin leeDatos() regresa por el
mecanismo de return el nmero de rectngulos, y recibir como parmetros las
direcciones de las variables xi y xf que se encuentran en la funcin main() y que guardan
los valores de xi y xf. La declaracin de la funcin leeDatos() tiene la forma:
int leeDatos(float *const pxi, float *const pxf);

La funcin calculaArea() regresa el valor del rea bajo la curva, un flotante. Esta
funcin requiere para poder calcular el rea de los valores de las abscisas inicial y final y
del nmero de rectngulos. Estos valores los debe recibir de la funcin main(), ya que es
quien la llama. La declaracin de calculaArea() queda como:
float calculaArea(float xi, float xf, int n);

La funcin escribeArea() no se modifica ni en su declaracin, ni en su definicin, ni en


su llamada.
La definicin de la funcin leeDatos() queda como:
int leeDatos(float *const pxi, float *const pxf) {
int n;
printf("\nEste programa tabula el area bajo la curva y = x^2,");
printf("\nentre las abscisas x = xi y x = xf. Utiliza el metodo");
printf("\ndel rectangulo\n");
printf("\nAbscisa inicial: ");
scanf("%f", pxi);
printf("\nAbscisa final: ");
scanf("%f", pxf);
printf("\nNumero de rectangulos: ");
scanf("%d", &n);
return n;
}

Recuerde que los parmetros pxi y pxf son apuntadores y la funcin scanf() requiere
que le pasemos las direcciones de las variables en que va a dejar los datos. Como esas

ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

133

direcciones estn en pxi y pxf, las llamadas a scanf() utiliza directamente a pxi y a
pxf como argumentos. La funcin calculaArea() tiene la siguiente definicin:
float calculaArea(float xi, float xf, int n) {
float base, sumaH, x, y, area;
sumaH = 0;
base = (xf - xi) / n;
for (x = xi; x < xf; x += base) {
y = x * x;
sumaH += y;
}
area = base * sumaH;
return area;
}

Aqu la nica diferencia es el que los valores de xi y xf le llegan a la funcin mediante los
parmetros en lugar de tomarlos de las variables externas.
La llamada a las funcin leeDatos() es:
nRect = leeDatos(&xi, &xf);

donde nRect es una variable entera en la que se almacena el nmero de rectngulos.


Recuerde que al llamar a la funcin hay que pasarle las direcciones de las variables xi,
xf. La llamada a la funcin calculaArea() es:
area = calculaArea(xi, xf, nRect);

Le estamos enviando los tres datos por parmetros. El programa completo se muestra a
continuacin:
/*
* File:
area5.c
* Author: mdomitsu
*
* Created on 4 de enero de 2010, 12:09 PM
*/
#include <stdio.h>
#include <stdlib.h>
/*
* Este programa tabula el area bajo la curva y = x^2, entre las
* abscisas x = xi y x = xf. Utiliza el mtodo del rectangulo.
*/
// Declaracin de funciones
int leeDatos(float *const pxi, float *const pxf);
float calculaArea(float xi, float xf, int n);
void escribeArea(float area);
int main(void) {
int nRect;
float xi, xf, area;

ITSON

Manuel Domitsu Kono

134

Apuntadores

// Lee xi, xf y n
nRect = leeDatos(&xi, &xf);
// Calcula el area
area = calculaArea(xi, xf, nRect);
// Escribe area
escribeArea(area);
return (EXIT_SUCCESS);
}
// Definicin de funciones
/*
* Esta funcin lee del teclado los valores de la abscisa inicial, xi,
* abscisa final, xf, y el nmero de rectngulos.
*
* Regresa el nmero de rectngulos y guarda en las variables apuntadas por
* los apuntadores pxi y pxf los valores de xi y xf.
*/
int leeDatos(float *const pxi, float *const pxf) {
int n;
printf("\nEste programa tabula el area bajo la curva y = x^2, entre las");
printf("\nabscisas x = xi y x = xf. Utiliza el metodo del rectangulo\n");
// Lee la abscisa inicial
printf("\nAbscisa inicial: ");
scanf("%f", pxi);
// Lee la abscisa final
printf("\nAbscisa final: ");
scanf("%f", pxf);
// Lee el nmero de rectngulos
printf("\nNumero de rectangulos: ");
scanf("%d", &n);
return n;
}
/*
* Esta funcion calcula el area bajo la curva y = x^2 entre las abscisas
* xi y xf, usando el metodo del paralelogramo con n rectngulos. La funcin
* recibe los valores de xi y xf y el nmero de rectangulos.
* La funcin regresa el area.
*/
float calculaArea(float xi, float xf, int n) {
float base, sumaH, x, y, area;
sumaH = 0;
// Calcula base
base = (xf - xi) / n;
// Para cada rectngulo
for (x = xi; x < xf; x += base) {
// Calculamos la altura del rectngulo
y = x * x;
// Acumulamos altura
sumaH += y;
}

ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

135

// Calcula el area
area = base * sumaH;
return area;
}
/*
* Esta funcin despliega en la pantalla el area bajo la curva.
* La funcin recibe el area.
*/
void escribeArea(float area) {
printf("\nEl area bajo la curva es %.8f\n\n", area);
}

Ejercicio sobre Apuntadores como Parmetros de Funciones


Reescribir el programa que calcula la media de las edades de un grupo de alumnos
(Ejercicio 2 sobre funciones, Captulo 6: Funciones) de tal manera que la funcin
leeDatos() regrese el nmero de alumnos por el mecanismo de return y la suma de
edades por parmetro apuntador.

Arreglos y Apuntadores
En el Captulo 7: Arreglos se vio que un arreglo es una lista de variables del mismo tipo
que se encuentran en localidades contiguas de memoria y que comparten el mismo
nombre. El hecho de que los elementos de un arreglo se encuentren en localidades
contiguas de memoria permite usar los apuntadores para implementar un mecanismo de
acceso a los elementos de un arreglo que, en algunos casos, es ms eficiente que el
indexamiento.

Arreglos Unidimensionales y Apuntadores


Supongamos que deseamos almacenar las calificaciones de un grupo de alumnos y para
ello declaramos el arreglo unidimensional:
int califs[50];

Si suponemos que la direccin del arreglo, es decir la direccin de la primera variable del
arreglo, califs[0] es 1000. La variable califs[1] tiene la direccin 1004, y as hasta
la ltima variable califs[49] que tiene la direccin 1196, como se muestra en la figura
8-5. Note que como el arreglo es de enteros, las direcciones de las variables se
incrementan de cuatro en cuatro.
Por otro lado, para almacenar las matrculas de esos alumnos podramos declarar el
arreglo
long mats[50];

ITSON

Manuel Domitsu Kono

136

Apuntadores

Si suponemos que la direccin del arreglo mats es 1200, las direcciones de los elementos
del arreglo sern los mostrados en la figura 8-6. Note que como el arreglo es de enteros
largos, las direcciones de las variables se incrementan de ocho en ocho.

Figura 8-5

Figura 8-6

Mecanismo Interno del Indexamiento para Arreglos Unidimensionales


En el Captulo 7: Arreglos se vio que el mecanismo de indexamiento permite acceder a
un elemento de un arreglo utilizando el nombre del arreglo y su posicin en el arreglo. Esta
posicin se conoce como el ndice del elemento. Este mecanismo de indexamiento se

ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

137

puede expresar en una segunda forma, la cual utiliza las direcciones de los elementos.
Dado que los elementos de un arreglo se encuentran en direcciones contiguas de
memoria, podemos expresar la direccin de un elemento en trminos de la direccin del
primer elemento del arreglo o direccin base del arreglo. La direccin de un elemento es
igual a la direccin base del arreglo ms un desplazamiento, un entero que representa la
distancia en nmero de elementos a la que se encuentra el elemento. Para el compilador,
el nombre del arreglo representa la direccin base del arreglo. Por ejemplo en el
arreglo califs, la direccin del primer elemento es &califs[0] o califs + 0 o
simplemente califs. La direccin del segundo elemento es &califs[1] o califs +
1, y la direccin del elemento 50 es &califs[49] o califs + 49, tal como se ilustra
en la figura 8-7.
Una vez que se tiene la direccin del elemento podemos accesar al contenido de ese
elemento mediante el operador de indireccin. As el acceso al dato almacenado en el
primer elemento del arreglo califs est dado por califs[0] o *califs, el acceso al
dato en el segundo elemento es califs[1] o *(califs+1), y el del ltimo elemento
por califs[49] o *(califs+49), tal como se muestra en la figura 8-7.

Figura 8-7
En el caso del arreglo mats, las direcciones y la forma de accesar a los diferentes
elementos se muestran en la figura 8-8.
Note que la direccin del elemento califs[0] es califs = 1000 y que la direccin de
califs[1] es califs+1 = 1004. Al sumarle uno a la direccin base del arreglo califs
vemos que la direccin se aumenta en cuatro bytes, apuntando correctamente al elemento
que se encuentra a un elemento de distancia del inicio del arreglo. Como el arreglo mats
es de largos, la direccin del elemento mats[1] es mats+1 = 1208. La razn por la cual
la suma se comporta en esta forma "extraa" es que la operacin de suma no es la misma

ITSON

Manuel Domitsu Kono

138

Apuntadores

que la operacin de suma de los nmeros enteros sino es una suma en la aritmtica de
apuntadores, y en est aritmtica al sumarle a una direccin un nmero entero n,
obtenemos la direccin del elemento que se encuentra a n elementos, del mismo tipo, ms
adelante.

Figura 8-8
El cdigo para sacar el promedio de las calificaciones usando este mtodo de accesar al
arreglo sera
suma = 0;
for(i = 0; i < nAlums; i++) suma += *(califs+i);
promedio = (float)suma/nAlums;

Las dos formas de indexamiento para acceder a los elementos de un arreglo son
equivalentes y pueden emplearse indistintamente, incluso pueden usarse ambos en el
mismo cdigo. En realidad el compilador transforma las expresiones que contienen ndices
en expresiones con direcciones base y desplazamientos. Por ejemplo la expresin
califs[i]

es transformada por el compilador a


*(califs+i)

Acceso a los Elementos de un Arreglo Unidimensional Usando


Apuntadores
Un segundo mecanismo de accesar a los elementos de un arreglo es declarar un
apuntador del mismo tipo del arreglo y usar este para que apunte al elemento que nos
interesa. Por ejemplo, el cdigo para sacar el promedio de las calificaciones usando este
mtodo de accesar al arreglo sera
ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

139

int *pCalifs = califs, *qCalifs;


suma = 0;
qCalifs = califs + nAlumnos;
for(; pCalifs < qCalifs; pCalifs++) suma += *pCalifs;
promedio = (float)suma/nAlumnos;

En este ejemplo hemos declarado dos apuntadores pCalifs que apunta al inicio del
arreglo califs y qCalifs que lo hacemos apuntar al siguiente elemento despus de la
ltima calificacin dentro del arreglo. En la expresin que controla el ciclo
pCalifs < qCalifs

estamos preguntndonos si la direccin almacenada en pCalifs es menor (o anterior) a


la direccin en qCalifs. Mientras no hayamos llegado al final de los datos seguiremos en
el ciclo. La expresin
pCalifs++

incrementa el valor de la direccin almacenada en pCalifs en uno, haciendo as que


apunte al siguiente elemento del arreglo. Por ltimo en la expresin
suma += *pCalifs

estamos usando el operador de indireccin para accesar al valor almacenado en la


direccin apuntada por pCalifs, accesando as a los elementos del arreglo.
Cuando se est procesando un arreglo en forma secuencial, el acceso a los elementos de
un arreglo usando este ltimo mtodo es ms eficiente que los dos anteriores. La razn de
esto la podemos ver si comparamos los cdigos usados para sumar las calificaciones
for(i = 0; i < nAlums; i++) suma += *(califs+i);
for(; pCalifs < qCalifs; pCalifs++) suma += *pCalifs;

Si contamos el nmero de sumas realizadas con el acceso usando ndices veremos que
se requieren 3 * nAlums sumas. Mientras que en el mtodo usando apuntadores tenemos
2 * nAlums sumas. Por lo que el segundo ciclo se ejecutar ms rpido.
Si estamos accesando el arreglo en forma aleatoria no hay ventaja en el mtodo de
apuntadores y probablemente el mtodo de ndices presente la ventaja de que su notacin
sea ms sencilla.
En C el concepto de arreglo y de apuntador estn ntimamente ligados. De hecho, si
tenemos un apuntador podemos indexar a este para accesar a un elemento que se
encuentre a una distancia dada de ste. Por ejemplo:

ITSON

Manuel Domitsu Kono

140

Apuntadores
int califs[MAX_GRUPO], *pCalifs = califs;
...
pCalifs[5] = 9;

le asigna 9 al sexto elemento del arreglo califs, tal como lo hara


califs[5] = 9;

Un ltimo aspecto que es importante mencionar es establecer la diferencia entre califs


y pCalifs. Al principio ambas apuntan al inicio del arreglo. Sin embargo, califs es una
direccin constante mientras que pCalifs es una variable apuntador. Por lo tanto
podemos hacer lo siguiente
pCalifs++;

pero lo que no podemos hacer es


califs++

Aritmtica de Apuntadores
En las secciones anteriores se estableci que exista una aritmtica de apuntadores cuyas
reglas diferan de la aritmtica de los nmeros enteros. Las operaciones vlidas en esta
aritmtica y sus reglas son las siguientes:

A una expresin de tipo apuntador le podemos sumar o restar una expresin entera
pexp {+|-} nexp

El resultado de est operacin es la direccin de un elemento del tipo apuntado por


pexp y que se encuentra a nexp elementos despus (si el operador es el de suma)
o antes (si el operador es el de resta) de la direccin dada por pexp. Por ejemplo,
supongamos que px es un apuntador y n es un entero, entonces las siguientes
expresiones son vlidas
px + 3
px++
--px
px -= 3 * n

Podemos restar dos expresiones de tipo apuntador siempre y cuando ambas


expresiones sean del mismo tipo (apunten al mismo tipo de dato).
pexp2 - pexp1

Esta operacin da como resultado el nmero de elementos del tipo apuntado por
pexp2 y pexp1 que se encuentran entre las direcciones pexp2 y pexp1. Por
ejemplo:

ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

141

int *px, int *py, n;


...
n = py - px;

el valor de n representa el nmero de localidades enteras que hay entre las


direcciones dadas por py y px.

Podemos comparar dos expresiones de tipo apuntador siempre y cuando ambas


expresiones sean del mismo tipo (apunten al mismo tipo de dato).
pexp1
pexp1
pexp1
pexp1
pexp1
pexp1

<
<=
>
>=
==
!=

pexp2
pexp2
pexp2
pexp2
pexp2
pexp2

La operacin < da como resultado verdadero (uno) si la direccin dada por pexp1
se encuentra antes que la direccin dada por pexp2 y falso (0) en caso contrario.
La operacin <= da como resultado verdadero (uno) si la direccin dada por pexp1
se encuentra antes o es la misma que la direccin dada por pexp2 y falso (0) en
caso contrario.
La operacin > da como resultado verdadero (uno) si la direccin dada por pexp1
se encuentra despus que la direccin dada por pexp2 y falso (0) en caso
contrario.
La operacin >= da como resultado verdadero (uno) si la direccin dada por pexp1
se encuentra despus o es la misma que la direccin dada por pexp2 y falso (0) en
caso contrario.
La operacin == da como resultado verdadero (uno) si la direccin dada por pexp1
es la misma que la direccin dada por pexp2y falso (0) en caso contrario.
La operacin != da como resultado verdadero (uno) si la direccin dada por pexp1
es diferente que la direccin dada por pexp2 y falso (0) en caso contrario.

Ninguna otra operacin es valida con apuntadores.

Apuntadores a Arreglos Unidimensionales como Parmetros de


Funciones
En el Captulo 7: Arreglos se vio que en C slo se permite que a una funcin se le pase la
direccin de un arreglo. Si la funcin conoce la direccin del arreglo, puede accesar a los
diferentes elementos del arreglo utilizando cualquiera de los dos mtodos ya vistos:

ITSON

Manuel Domitsu Kono

142

Apuntadores

indexamiento o apuntadores. Ya que el parmetro es una direccin, se puede utilizar un


apuntador y la sintaxis para el parmetro es la siguiente:
tipo *nomParArr

Recuerde que el parmetro slo representa la direccin del arreglo pero no contiene la
informacin sobre el tamao del arreglo. Si se requiere que la funcin conozca el tamao
del arreglo deberemos proporcionrsela a travs de otro parmetro.
Al igual que en la declaracin de apuntadores, la declaracin de un parmetro que
representa la direccin de un arreglo puede contener el calificador const para indicar que
la direccin del arreglo, los datos del arreglo o ambos no pueden modificarse. En la
siguiente declaracin
tipo *const nomParArr

estamos indicando que dentro de la funcin no podemos alterar el valor del parmetro
nomParArr el cul representa la direccin del arreglo. Por otro lado la declaracin
const tipo *nomParArr

indica que los valores del arreglo apuntado por nomParArr no pueden modificarse. Por
ltimo, la declaracin
const tipo *const nomParArr

indican que dentro de la funcin no podemos alterar el valor del parmetro nomParArr, la
direccin del arreglo, y que los valores del arreglo apuntado por nomParArr tampoco
pueden modificarse.
Al llamar a la funcin, el argumento ser la direccin del arreglo, el cual puede escribirse
de cualquiera de las siguientes formas:
&nomArr[0]

nomArr

La segunda forma es la forma ms empleada. Aunque normalmente al llamar a una


funcin y pasarle la direccin de un arreglo le pasamos la direccin de inicio del arreglo,
podemos pasarle la direccin de un subarreglo del arreglo. Por ejemplo podemos llamar a
la funcin de las formas siguientes
&nomArr[3]

nomArr + 3

para pasarle la direccin del cuarto elemento del arreglo, posiblemente lo que deseamos
es que la funcin procese del cuarto elemento del arreglo en adelante.

ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

143

Ejemplo sobre Apuntadores a Arreglos Unidimensionales como


Parmetros de Funciones
Crear una funcin que lea las matrculas (nmeros enteros largos) y las calificaciones (tipo
enteros) de los alumnos de un grupo y las almacene en dos arreglos unidimensionales. La
funcin deber leer datos hasta que se de una matrcula cero la cual indica fin de captura.
La funcin regresa el nmero de alumnos por el mecanismo de return. La declaracin y la
definicin de la funcin pedida son:
int leeDatos(long *pMatriculas, int *pCalifs, int tamMaxGpo);
/*
* Esta funcion lee las matriculas y las calificaciones de los alumnos
* de un grupo y los almacena en los arreglos apuntados por pMatriculas
* y pCalifs. El tamao maximo de los arreglos es de tamMaxGpo. La funcion
* regresa el numero de alumnos.
*/
int leeDatos(long *pMatriculas, int *pCalifs, int tamMaxGpo) {
int n = 0;
printf("\nEste programa lee las matriculas y las calificaciones");
printf("\nde un grupo. El programa ordena los datos por la matricula");
printf("\ny los despliega. El programa tambien permite consultar");
printf("\nla calificacion por matricula.\n");
while (1) {
// Verifica si hay espacio en los arreglos
if (n >= tamMaxGpo) {
printf("\nArreglos llenos");
break;
}
// Lee una matricula
printf("\nMatricula del alumno %d, 0 para terminar: ", n + 1);
scanf("%ld", pMatriculas);
// Si la matricula leida es cero termina
if (*pMatriculas == 0) break;
// Lee la calificacion
printf("\nCalificacion del alumno %d: ", n + 1);
scanf("%d", pCalifs);
// Siguiente alumno
n++;
pMatriculas++;
pCalifs++;
}
// Regresa el numero de alumnos
return n;
}

Ejercicios sobre arreglos unidimensionales


1.

ITSON

Crear una funcin que despliegue el contenido de los arreglos del ejemplo 1. Utilice
el mecanismo de apuntadores para acceder a los elementos de los arreglos.

Manuel Domitsu Kono

144

2.

Apuntadores

Crear una funcin que busque secuencialmente una matrcula en el arreglo de


matrculas del ejemplo 1. La funcin deber regresar un entero, la posicin de la
matrcula si la encontr o -1 si no se encuentra. Utilice el mecanismo de
apuntadores para acceder a los elementos de los arreglos.

Arreglos Multidimensionales y Apuntadores


En el Captulo 7: Arreglos se vio que un arreglo en dos dimensiones es un arreglo de
arreglos de una dimensin y que puede visualizarse como una tabla o matriz, mientras
que un arreglo en tres dimensiones es un arreglo de tablas. Por ejemplo para almacenar
las cuatro calificaciones parciales de un grupo de alumnos podramos declarar el arreglo:
int calPars[50][4]

que declara un arreglo llamado calPars formado por 50 variables (una para cada
alumno) que a su vez son arreglos de 4 elementos de tipo int (las calificaciones). Otra
forma de ver al arreglo es como una tabla con 50 filas (los alumnos) y con cuatro
columnas (las calificaciones) en cada fila. Esta declaracin nos crea 200 variables como
se muestran en la figura 8-9. En realidad, la figura 8-9 slo es una representacin del
arreglo. En la memoria de la computadora, el arreglo se encuentra como se muestra en la
figura 8-10. Note que los elementos se encuentran ordenados por filas. Primero los
elementos en la primera fila, luego los de la segunda fila, etc.

Figura 8-9
Por otro lado, supongamos que deseamos almacenar las ventas por semestre de una
compaa que est establecida en tres ciudades, con cuatro sucursales en cada ciudad.
Para esto podemos declarar el arreglo ventas
float ventas[3][4][2];

como formado por 3 variables (las ciudades) que a su vez son arreglos en dos
dimensiones. Cada uno de los elementos es un arreglo de 4 (sucursales) por 2 elementos
ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

145

(semestres) de tipo float. Otra forma de ver al arreglo es como uno formado de 3 tablas
(las ciudades), cada una con 4 filas (las sucursales) y cada fila con 2 columnas (los
semestres). Esta declaracin nos crea 24 variables tal como se muestran en la figura 8-11.

Figura 8-10

Figura 8-11
Al igual que con los arreglos en dos dimensiones, en los arreglos en tres dimensiones los
elementos se encuentran ordenados primero por tablas y luego por filas, tal como se
muestra en la figura 8-12.

Mecanismo Interno del Indexamiento para Arreglos


Multidimensionales
Al igual que con los arreglos en una dimensin, el mecanismo de indexamiento para los
arreglos multidimensionales se puede expresar en trminos de las direcciones de los
elementos. Un arreglo en dos dimensiones es un arreglo de arreglos. El nombre de cada

ITSON

Manuel Domitsu Kono

146

Apuntadores

arreglo est compuesto del nombre del arreglo completo y su posicin en ste. Por
ejemplo el arreglo calPars es un arreglo de 50 arreglos y sus nombres son
calPars[0], calPars[1], ... , calPars[49]

Figura 8-12
Como calPars[0] es el nombre del arreglo que contiene las calificaciones del primer
alumno, calPars[0] representa la direccin de ese arreglo o fila. calPars[1]
representa la direccin de la segunda fila, etc, tal como se muestra en la figura 8-13.
En un arreglo unidimensional, el acceso a un elemento por indexamiento se puede escribir
de dos formas, por ejemplo en el arreglo califs de la figura 8-7, califs[i] es
equivalente a *(califs+i). Por consistencia, en un arreglo en dos dimensiones las
direcciones de las filas tambin se pueden escribir como:
*(calPars+1), *(calPars+2), ... , *(calPars+49)

como se muestran en la figura 8-13.

ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

147

Figura 8-13
Cada elemento de un arreglo en dos dimensiones, tiene tambin una direccin. Esta
direccin se puede establecer a partir de la direccin de la fila en la que se encuentra el
elemento. Como la fila constituye un arreglo unidimensional, la direccin del elemento es
la direccin base de la fila ms el desplazamiento del elemento. Por ejemplo en el arreglo
calPars, figura 8-13, la direccin del j-simo elemento de la fila i es
calPars[i]+j

*(calPars+i)+j

Si a esta direccin le aplicamos el operador de indireccin obtenemos el acceso a dicho


elemento. Luego el elemento calPars[i][j] puede accesarse mediante
*(calPars[i]+j)

*(*(calPars+i)+j)

En forma similar, el arreglo ventas es un arreglo de arreglos en dos dimensiones o tablas.


Los nombres de las tablas son
ventas[0], ventas [1] y ventas[2]

Estos nombres son las direcciones de cada plano y pueden escribirse, por similitud a los
arreglos unidimensionales, como
*(ventas+0), *(ventas+1) y *(ventas+2)

tal como se muestra en la figura 8-14. Cada una de las tablas est formada por un arreglo
de 4 filas. Estas filas tienen su nombre. Por ejemplo la fila j del plano i tiene el nombre
ventas[i][j]

ITSON

Manuel Domitsu Kono

148

Apuntadores

Figura 8-14
Para mantener una consistencia con la notacin empleada en los arreglos de dos
dimensiones, el nombre de la fila anterior, puede escribirse de las siguientes otras formas
*(ventas[i]+j) o *(*(ventas+i)+j)

El nombre de cada fila es la direccin de la fila. Tambin cada elemento del arreglo tiene
su direccin, esta se puede formar a partir de la direccin de la fila. Por ejemplo el
elemento k de la fila j de la tabla i del arreglo tiene la direccin dada por
ventas[i][j]+k o *(ventas[i]+j)+k o *(*(ventas+i)+j)+k

Por ltimo, el acceso a los elementos del arreglo se obtiene aplicndole el operador de
indireccin a cualquiera de las formas de expresar la direccin de un elemento. Luego el
elemento ventas[i][j][k] puede accesarse mediante
*(ventas[i][j]+k) o *(*(ventas[i]+j)+k) o *(*(*(ventas+i)+j)+k)

Las diferentes formas descritas anteriormente para accesar a los elementos de un arreglo
multidimensional son equivalentes y pueden emplearse indistintamente, incluso pueden
usarse mezcladas en el mismo cdigo. Estas formas construyen la direccin de un
elemento a partir de la direccin de la fila y sta ltima direccin a partir de la direccin de
una tabla, etc.
Por ejemplo el siguiente cdigo nos permite calcular el promedio de las calificaciones de
cada alumno y la calificacin promedio del grupo:
int calPars[50][4];
int i, j, suma, nAlums;

ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

149

float promedios[50], sumaT, promedioT;


...
sumaT = 0;
// Para cada alumno
for(i = 0; i < nAlums; i++) {
// Inicializa el acumulador de calificaciones del alumno
suma = 0;
// Acumula las calificaciones del alumno
for(j = 0; j < 4; j++) suma += *(*(calPars+i)+j);
/* Calcula el promedio del alumno y almacnalo
en el arreglo promedios */
*(promedios+i) = (float)suma/4;
// Acumula el promedio
sumaT += *(promedios+i);
}
// Calcula el promedio global de los alumnos
promedioT = sumaT/nAlums;

Otra alternativa es construir la direccin de un elemento a partir de la direccin base del


arreglo y del desplazamiento de dicho elemento con respecto al inicio del arreglo. Esta
tcnica se basa en el hecho de que los arreglos multidimensionales se encuentran en la
memoria de la computadora acomodados como una lista en una dimensin, tal como se
muestra en las figuras 8-10 y 8-12.
Considerando a los arreglos multidimensionales como lineales, la direccin de un
elemento se formara a partir de la direccin base del arreglo ms un desplazamiento. La
direccin base de un arreglo multidimensional es la direccin de su primer elemento. Esta
direccin, a diferencia de los arreglos unidimensionales no est dada por el nombre del
arreglo. En un arreglo en dos dimensiones el nombre del arreglo, por s solo, constituye la
direccin de la primera fila del arreglo y aunque la direccin del primer elemento del
arreglo y la direccin de la primera fila son numricamente iguales, conceptualmente son
diferentes. La razn de esto est dada por forma en que opera la aritmtica de
apuntadores.
Por ejemplo considere el arreglo calPars. Tanto &calPars[0][0] como calPars
valen 1200 (la direccin de inicio del arreglo), sin embargo &calpars[0][0]+1 vale
1204 (la direccin del siguiente elemento del arreglo) mientras calpars+1 vale 1216 (la
direccin de la siguiente fila del arreglo).
De la misma manera, en un arreglo en tres dimensiones el nombre del arreglo, por s solo,
constituye la direccin de la primera tabla del arreglo y no del primer elemento del arreglo,
aunque stas sean numricamente iguales. Por ejemplo en el arreglo ventas, tanto
&ventas[0][0][0] como ventas valen 1500 (la direccin de inicio del arreglo), sin
embargo &ventas[0][0][0]+1 vale 1504 (la direccin del siguiente elemento del
arreglo) mientras ventas+1 vale 1532 (la direccin de la siguiente tabla del arreglo).

ITSON

Manuel Domitsu Kono

150

Apuntadores

Para determinar el desplazamiento de un elemento de un arreglo multidimensional con


respecto al inicio del arreglo tenemos que considerar que el arreglo en la memoria es
lineal y que debemos de contar todos los elementos que se encuentran entre el elemento
dado y el primer elemento del arreglo. Por ejemplo, en un arreglo de dos dimensiones, el
desplazamiento del elemento que se encuentra en la fila i, columna j est dado por
i * numCols + j

donde numCols es el nmero de columnas en el arreglo. En un arreglo de tres


dimensiones, el desplazamiento del elemento que se encuentra en la tabla i, fila j, columna
k est dado por
i * numFilas * numCols + j * numCols + k

donde numFilas es el nmero de filas y numCols es el nmero de columnas en el


arreglo. Por ejemplo el siguiente cdigo nos permite calcular el promedio de las
calificaciones de cada alumno y la calificacin promedio del grupo:
int calPars[50][4];
int i, j, suma;
float promedios[50], sumaT, promedioT;
...
sumaT = 0;
// Para cada alumno
for(i = 0; i < nAlums; i++) {
// Inicializa el acumulador de calificaciones del alumno
suma = 0;
// Acumula las calificaciones del alumno
for(j = 0; j < 4; j++)
suma += &calPars[0][0] + 4*i +j;
/* Calcula el promedio del alumno y almacnalo
en el arreglo promedios */
*(promedios+i) = (float)suma/4;
// Acumula el promedio
sumaT += *(promedios+i);
}
// Calcula el promedio global de los alumnos
promedioT = sumaT/nAlums;

Acceso a los Elementos de un Arreglo Multidimensional Usando


Apuntadores
El mecanismo de apuntadores empleado para accesar a los elementos de un arreglo
unidimensional puede emplearse en el caso de arreglos multidimensionales. Podemos
declarar un apuntador al mismo tipo que el tipo base del arreglo e inicializarlo a la
direccin base del arreglo. Incrementando este apuntador podemos hacer que apunte a
los diferentes elementos del arreglo. Por ejemplo, el siguiente cdigo es una variante del
ejemplo anterior.

ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

151

int calPars[50][4], *pCalPars, *qCalPars;


int i, suma;
float promedios[50], sumaT, promedioT, *pprom = promedios;
...
suma = 0;
sumaT = 0;
// Apuntador al inicio del arreglo
pCalPars = &calPars[0][0];
/* Apuntador a la siguiente localidad despus del
final del arreglo + 1 */
qCalPars = &calPars[nAlums][0];
for(i = 0 ; pCalPars < qCalPars; pCalPars++, i++) {
// Acumula las calificaciones de un alumno
suma += *pCalPars;
// Si ya se acumularon las calificaciones de un alumno
if(i % 4 == 3) {
// Calcula el promedio de ese alumno
*pprom = (float)suma/4;
// Acumula el promedio
sumaT += *pprom;
pprom++;
suma = 0;
}
}
// Calcula el promedio global de los alumnos
promedioT = sumaT/nAlums;

En el cdigo anterior, en la asignacin


pCalPars = &calPars[0][0];

hacemos que pCalPars apunte al inicio del arreglo. Si hubisemos escrito


pCalPars = calPars;

el compilador generara una advertencia, ya que pCalPars es un apuntador a int y


calPars, por ser el nombre de un arreglo en dos dimensiones, es la direccin de un
arreglo de arreglos y que aunque apuntan a la misma direccin son direcciones a diferente
tipo. Sin embargo como en una asignacin el tipo resultante es el de la variable de la
izquierda, la direccin calPars es convertida a una direccin a entero. El resultado es de
que pCalPars si apunta a la direccin base del arreglo, aunque el compilador nos
advierte que hemos hecho una operacin sospechosa y que pudiera ser errnea.
Podemos evitar la advertencia del compilador haciendo clara nuestra intencin del cambio
de tipo de la direccin, usando el operador cast
pCalPars = (int *)calPars;

ITSON

Manuel Domitsu Kono

152

Apuntadores

Aqu, estamos convirtiendo explcitamente, el tipo apuntado por calPars a un apuntador


a entero.

Apuntadores a Arreglos Multidimensionales como Parmetros de


Funciones
En el Captulo 7: Arreglos se vio que a una funcin slo le podemos pasar la direccin de
un arreglo multidimensional. Si el arreglo es en dos dimensiones la declaracin del
parmetro tendr la siguiente forma:
tipo nomParArr[][numCols]

Para un arreglo en tres dimensiones sera


tipo nomParArr[][numFilas][numCols]

Las declaraciones correspondientes usando apuntadores seran:


tipo *nomParArr[numCols]

Para un arreglo en dos dimensiones, y


tipo *nomParArr[numFilas][numCols]

Para un arreglo en tres dimensiones.


Ambas declaraciones del parmetro que corresponde a la direccin del arreglo en dos
dimensiones slo proporcionan la direccin del arreglo y su nmero de columnas. Si se
requiere que la funcin conozca el nmero de filas deberemos proporcionrsela a travs
de otro parmetro. En el caso del arreglo en tres dimensiones, las declaraciones slo
proporcionan la direccin del arreglo y sus nmeros de filas y columnas. Si se requiere
que la funcin conozca el nmero de tablas deberemos proporcionrsela a travs de otro
parmetro.
Al llamar a la funcin, el argumento ser slo el nombre del arreglo,
nomArr

Otra alternativa para declarar el parmetro que corresponde a la direccin de un arreglo


multidimensional es la siguiente
tipo *nomParArr

En este caso el parmetro slo proporciona la direccin del base del arreglo y deberemos
utilizar otros parmetros para pasarle a la funcin las dimensiones del arreglo. Adems en
este caso la direccin de los elementos debe calcularse usando el mecanismo de
apuntadores.

ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

153

En este caso, al llamar a la funcin, el argumento ser la direccin base del arreglo,
&nomArr[0][0]

en el caso de un arreglo en dos dimensiones


&nomArr[0][0][0]

en el caso de un arreglo en tres dimensiones, etc.

Ejemplo sobre arreglos multidimensionales


Una agencia de autos usados se especializa en automviles Volkswagen de los siguientes
tipos: Pointer, Golf y Jetta. Adems slo maneja los modelos de los tres aos recientes.
1. Para tener el control del inventario, el dueo guarda en un arreglo de dos
dimensiones, el nmero de automviles que posee. Escribe una funcin llamada
leeAutos() que permita capturar el inventario inicial del lote y lo almacene en un
arreglo. La funcin recibe como parmetro la direccin del arreglo en el que se va a
almacenar los datos.
Si el parmetro representa un apuntador al arreglo en dos dimensiones, la
declaracin y la definicin de la funcin pedida pueden ser
/* Constante que representa el modelo de auto ms viejo
vendido en la agencia */
const int VIEJO = 2007;
// Declaracin de la funcin leeAutos()
void leeAutos(int *pAutos[3]);
// Definicin de la funcin leeAutos()
/*
* Esta funcin lee el nmero de autos de cada marca y
* modelo de la agencia de autos usados y los almacena
* en el arreglo apuntado por pAutos.
*/
void leeAutos(int (*pAutos)[3]) {
int marca, modelo;
// Para cada marca de auto
for (marca = 0; marca < 3; marca++) {
// Para cada modelo
for (modelo = 0; modelo < 3; modelo++) {
// Despliega la marca
switch (marca) {
case 0: printf("\nAutos Pointer, ");
break;
case 1: printf("\nAutos Golf, ");
break;
case 2: printf("\nAutos Jetta, ");
}
// Despliega el modelo
printf("modelo %d: ", VIEJO + modelo);

ITSON

Manuel Domitsu Kono

154

Apuntadores

// Lee la cantidad de autos de la marca y modelo


scanf("%d", pAutos[marca]+modelo);
}
}
}

Por otro lado, si el parmetro de la funcin, slo representa la direccin base del
arreglo, la declaracin y la definicin de la funcin pedida pueden ser
/* Constante que representa el modelo de auto ms viejo
vendido en la agencia */
const int VIEJO = 2007;
// Declaracin de la funcin leeAutos()
void leeAutos(int *pAutos);
// Definicin de la funcin leeAutos()
/*
* Esta funcin lee el nmero de autos de cada marca y
* modelo de la agencia de autos usados y los almacena
* en el arreglo apuntado por pAutos.
*/
void leeAutos(int *pAutos) {
int marca, modelo;
// Para cada marca de auto
for (marca = 0; marca < 3; marca++) {
// Para cada modelo
for (modelo = 0; modelo < 3; modelo++) {
// Despliega la marca
switch (marca) {
case 0: printf("\nAutos Pointer, ");
break;
case 1: printf("\nAutos Golf, ");
break;
case 2: printf("\nAutos Jetta, ");
}
// Despliega el modelo
printf("modelo %d: ", VIEJO + modelo);
// Lee la cantidad de autos de la marca y modelo
scanf("%d", pAutos + 3*marca + modelo);
}
}
}

2. A fin de saber la inversin que tiene en la agencia, el dueo guarda en otro arreglo,
el monto que pago por cada automvil (Supongamos que por todos los autos del
mismo tipo y del mismo modelo pag lo mismo). Escribe una funcin llamada
leeMontos() que permita capturar el monto pagado por cada automvil y lo
almacene en un arreglo. La funcin recibe como parmetro la direccin del arreglo
en que se va a almacenar los datos.
Si el parmetro representa un apuntador al arreglo en dos dimensiones, la
declaracin y la definicin de la funcin pedida pueden ser
// Declaracin de la funcin leeMontos()

ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

155

void leeMontos(float *pMontos[3]);


// Definicin de la funcin leeMontos()
/*
* Esta funcin lee el costo de cada auto para cada marca y
* modelo de la agencia de autos usados y los almacena
* en el arreglo apuntado por pMontos.
*/
void leeMontos(float (*pMontos)[3]) {
int marca, modelo;
// Para cada marca de auto
for (marca = 0; marca < 3; marca++) {
// Para cada modelo */
for (modelo = 0; modelo < 3; modelo++) {
// Despliega la marca
switch (marca) {
case 0: printf("\nAutos Pointer, ");
break;
case 1: printf("\nAutos Golf, ");
break;
case 2: printf("\nAutos Jetta, ");
}
// Despliega el modelo
printf("modelo %d: ", VIEJO + modelo);
// Lee los montos de los autos de la marca y modelo
scanf("%f", pMontos[marca]+modelo);
}
}
}

Por otro lado, si el parmetro de la funcin, slo representa la direccin base del
arreglo, la declaracin y la definicin de la funcin pedida pueden ser
// Declaracin de la funcin leeMontos()
void leeMontos(float *pMontos);
// Definicin de la funcin leeMontos()
/*
* Esta funcin lee el costo de cada auto para cada marca y
* modelo de la agencia de autos usados y los almacena
* en el arreglo apuntado por pMontos.
*/
void leeMontos(float *pMontos) {
int marca, modelo;
// Para cada marca de auto
for (marca = 0; marca < 3; marca++) {
// Para cada modelo */
for (modelo = 0; modelo < 3; modelo++) {
// Despliega la marca
switch (marca) {
case 0: printf("\nAutos Pointer, ");
break;
case 1: printf("\nAutos Golf, ");
break;
case 2: printf("\nAutos Jetta, ");
}
// Despliega el modelo
printf("modelo %d: ", VIEJO + modelo);

ITSON

Manuel Domitsu Kono

156

Apuntadores

// Lee los montos de los autos de la marca y modelo


scanf("%f", pMontos + 3*marca + modelo);
}
}
}

Ejercicios sobre arreglos multidimensionales


1. Haga una funcin llamada montoTotal() que devuelva el monto total invertido en
el inventario del lote. La funcin recibe como parmetros las direcciones de los
arreglos que contienen el nmero de autos y sus costos. En esta funcin utilice como
parmetros apuntadores a los arreglos en dos dimensiones.
2. Modifique la funcin del ejercicio 1 para que el parmetro slo contenga la direccin
del arreglo sin la informacin de las columnas del arreglo.

Problemas
1. Cree una funcin que reciba un entero largo que represente el nmero de
segundos transcurrido desde las 0:00:00 y que regrese la hora del da en tres
enteros que representen las horas, minutos y segundos, respectivamente.
2. Escribe la salida que tendra el siguiente programa.
#include <stdio.h>
void f1(int *u, int *d, int *t);
void f2(int *n1, int *n2);
int main(void) {
int x, y, z;
x = 90; y = 25; z = 50;
f1(&x, &y, &z);
printf("\n%d
%d
%d", x, y, z);
x = 6; y = 2; z = 1;
f1(&x, &y, &z);
printf("\n%d
%d
%d", x, y, z);
return 0;
}
void f1(int *u, int *d, int *t) {
if(*u > *d) f2(u, d);
if(*d > *t) {
f2(d, t);
if(*u > *d) f2(u, d);
}
}
void f2(int *n1, int *n2) {
int t = *n1;
*n1 = *n2;

ITSON

Manuel Domitsu Kono

Captulo 8

Apuntadores

157

*n2 = t;
}

3. Cree una funcin que reciba como parmetros dos apuntadores a nmeros
enteros. Esos nmeros representan el numerador y denominador de un nmero
fraccionario. La funcin deber reducir ese nmero fraccionario a su equivalente
mas simple y regresar el nmero fraccionario de la misma forma que lo recibi. Por
ejemplo el equivalente ms simple del nmero 24/120 es 1/5. Sugerencia: La
funcin puede llamar a la funcin que calcula el mximo comn divisor de dos
nmeros, del problema 3 del Captulo 6: Funciones.
4. a) Cree una funcin que reciba como parmetros dos apuntadores a nmeros
enteros y cuatro enteros. Los cuatro enteros representan dos nmeros
fraccionarios tal como se explic en el problema anterior. La funcin deber
sumar las fracciones y regresar su equivalente ms simple a travs de los dos
parmetros apuntador. b) Repita el inciso a) para la resta, multiplicacin y
divisin de fracciones.
5. Crea un programa que permita el despliegue de una tabla de conversin de C a F
o de F a C. El programa deber tener al menos cuatro funciones:
Una que pregunte el tipo de conversin deseada. El usuario teclear 'c' o 'C' si la
conversin va a ser de C a F y 'f' o 'F' si la conversin es de F a C. La funcin
deber regresar el carcter ledo por el mecanismo de return.
Otra funcin que pregunte el rango de valores de la tabla y el incremento. La
funcin recibir como parmetros las direcciones de las variables donde se
almacenarn estos datos.
Otras dos funciones para desplegar cada tipo de tabla de conversin.
6. Escribe el cdigo de un programa, que realice las cuatro operaciones
fundamentales de la aritmtica sobre fracciones. El programa deber ser capaz de
realizar operaciones consecutivas, mostrando los resultados parciales. Si
deseamos que el programa termine presionaremos la tecla '=', en lugar del
operador.
Una primera aproximacin al pseudocdigo es:
lee resultado
// Primer nmero fraccionario
lee operador
mientras(operador != '=') {
lee operando
// Siguiente nmero fraccionario
calcula resultado
escribe resultado
lee operador
}

ITSON

Manuel Domitsu Kono

158

Apuntadores

Los nmeros fraccionarios debern leerse y desplegarse en el formato natural, esto


es, tal como la escribimos normalmente, por ejemplo:
2/3
Use las funciones desarrolladas en el problema 4.
7. En el contexto de la declaracin:
float tabla[10], *pt, *qt;

Cul es el efecto de las siguientes declaraciones y proposiciones?


pt = tabla;
*pt = 0;
*(pt + 2) = 3.14;
qt = tabla + 10;
printf("\n%d", qt - pt);

8. Modifique el programa del problema 1 (del final del captulo) del Captulo 7:
Arreglos para que el acceso a los elementos de los arreglos sea mediante
apuntadores en lugar de ndices.
9. Modifique el programa del problema 2 (del final del captulo) del Captulo 7:
Arreglos para que el acceso a los elementos de los arreglos sea mediante
apuntadores en lugar de ndices.
10. Modifique el programa del problema 3 (del final del captulo) del Captulo 7:
Arreglos para que el acceso a los elementos de los arreglos sea mediante
apuntadores en lugar de ndices.
11. Modifique el programa del problema 4 (del final del captulo) del Captulo 7:
Arreglos para que el acceso a los elementos de los arreglos sea mediante
apuntadores en lugar de ndices.

ITSON

Manuel Domitsu Kono