Anda di halaman 1dari 88

MIGUEL .

TOLEDO MARTNEZ

CONTENIDO DE LA LECCIN 20
APUNTADORES Y CADENAS
1. Introduccin
2. Declaracin e iniciacin de variables de apuntador
3. Operadores de los apuntadores
3.1. Ejemplo 20.1

4. Llamado de funciones por referencia


4.1. Ejemplo 20.2

5. Empleo del calificador const con apuntadores


5.1. Ejemplo 2 20.3, 20.4, 20.5, 20.6, 20.7

6. Ordenamiento de burbuja mediante llamada por referencia


6.1. Ejemplos 20.8, 20.9, 20.10

7. Expresiones de apuntadores y aritmtica de apuntadores


8. Relacin entre apuntadores y arreglos
8.1. Ejemplos 20.11, 20.12

9. Arreglos de apuntadores
10. Caso de estudio: Simulacin de barajado y reparticin de naipes
10.1. Ejemplo 20.13

3
37
4
6

6
7

10
11

15
15

18
21
22

24
25
28

11. Apuntadores y funciones

29

11.1. Ejemplos 20.14, 20.15

29

12. Introduccin al procesamiento de caracteres y cadenas


12.1. Fundamentos de los caracteres y las cadenas
12.2. Funciones de manipulacin de cadenas de la biblioteca de manejo de cadenas
12.2.1. Ejemplo 20.16, 20.17, 20.18, 20.19, 20.20

12.3. Ejercicios resueltos


12.4. Funciones de cadena que utilizan cadenas ubicadas fuera de los 64 kb (far string)
12.5. Nmero de ocurrencias de un carcter dentro de una cadena
12.6. Contar el nmero de ocurrencia de una subcadena dentro de una cadena
12.7. Obtener un ndice a una subcadena
12.8. Obtener la ocurrencia mas a la derecha de una subcadena
12.9. Remover una subcadena contenida dentro de una cadena
12.10. Reemplazo de una subcadena por otra
12.11. Determinar si un carcter es alfanumrico
12.12. Determinar si un carcter es una letra del alfabeto
12.13. Determinar si un carcter contiene un valor ASCII
12.14. Determinar si un carcter es un carcter de control
12.15. Determinar si un carcter es un digito
12.16. Determinar si un carcter es un carcter grfico
12.17. Determinar si un carcter es mayscula o minscula
12.18. Determinar si un carcter es imprimible
12.19. Determinar si un carcter es un smbolo de puntuacin
12.20. Determinar si un carcter es el carcter espacio
12.21. Determinar si un carcter es un valor hexadecimal
12.22. Carcter ASCII vlido

13. Pensando en objetos: Iteraciones entre los objetos


14. Errores comunes de programacin
15. Buenas prcticas de programacin
16. Propuestas de desempeo
APUNTADORES Y CADENA LECCIN 20

33
33
34
36

40
47
47
53
54
54
54
55
56
56
56
56
56
57
57
57
57
58
58
59

64
65
66
67
20-1

MIGUEL . TOLEDO MARTNEZ

17. Sugerencias de portabilidad


18. Observaciones de Ingeniera de Software
19. Indicaciones de prueba y depuracin
20. Lo que necesita saber
21. Preguntas y problemas
21.1. Preguntas
21.2. Problemas
21.2.1.
21.2.2.
21.2.3.
21.2.4.
21.2.5.

Seccin especial: construya su propia computadora


Ms problemas de apuntadores
Problemas de manipulacin de cadenas
Seccin especial: manipulacin avanzada de cadenas
Interesante proyecto de manipulacin de cadenas

APUNTADORES Y CADENA LECCIN 20

67
67
67
68
70
70
73
75
79
84
85
88

20-2

MIGUEL . TOLEDO MARTNEZ

LECCIN 20
APUNTADORES Y CADENAS
INTRODUCCIN
En esta leccin se estudiar una de las caractersticas ms poderosas del lenguaje de
programacin C++: el apuntador. Los apuntadores son una de las capacidades de C++ ms
difciles de dominar. En otra leccin vimos que las referencias pueden servir para hacer llamadas
por referencia. Los apuntadores permiten a los programas simular la llamada por referencia y
crear y manipular estructuras dinmicas de datos, es decir, estructuras de datos que pueden crecer
y encogerse, como las listas vinculadas, colas, pilas y rboles. Esta leccin explica los conceptos
bsicos acerca de los apuntadores. Tambin refuerza la relacin ntima entre los arreglos, los
apuntadores y las cadenas e incluye un amplio conjunto de ejemplos de procesamiento de
cadenas.
En otro semestre se estudiarn el empleo de los apuntadores con estructuras. La
programacin orientada a objetos se efecta con apuntadores y referencias. En ese semestre se
presentan tcnicas de administracin dinmica de memoria y ejemplos de creacin y uso de
estructuras dinmicas de datos.
El enfoque de los arreglos y cadenas como apuntadores se deriva de C. En la especialidad
de computacin se estudiarn los arreglos y las cadenas como objetos en toda forma.
Los objetivos de esta leccin son:

Aprender a utilizar los apuntadores.


Emplear los apuntadores para pasar argumentos a las funciones mediante llamada por referencia.
Entender la estrecha relacin entre los apuntadores, los arreglos y las cadenas.
Comprender la utilidad de los apuntadores a funciones.
Declarar y utilizar arreglos de cadenas.
Asignar caracteres a una cadena de caracteres.
Comprender la importancia del carcter NULL.
Inicializar cadena de caracteres.
Pasar cadena de caracteres a una funcin.
Utilizar las funciones de las libreras en tiempo de ejecucin para manejar cadenas de caracteres.

DECLARACIN E INICIACIN DE VARIABLES DE APUNTADOR


Las variables de apuntador contienen direcciones de memoria como sus valores.
Normalmente las variables contienen valores especficos. Por otra parte, los apuntadores
contienen direcciones de variables que contienen valores especficos. En este sentido, los
nombres de variables hacen referencia directa a un valor y los apuntadores hacen referencia
indirecta a un valor (figura 20.1) La referencia a un valor a travs de un apuntador se llama
indireccin.

APUNTADORES Y CADENA LECCIN 20

20-3

MIGUEL . TOLEDO MARTNEZ

contador
7
contadorPtr

contador hace referencia directa a una


variable cuyo valor es 7

contador
7

contadorPtr hace referencia indirecta a una


variable cuyo valor es 7.

Figura 20.1. Referencias directa e indirecta a una variable

Los apuntadores, como cualquier otra variable, se deben declarar antes de utilizarlos. La
declaracin:
int *contadorPtr, contador;

declara la variable contadorPtr como de tipo int * (es decir, como un apuntador a un valor
entero) y se lee contadorPtr es un apuntador a int o contadorPtr apunta a un objeto de clase
entero. Adems, la variable contador se declara como entero, no como apuntador a un entero. En
esta declaracin, el * solamente se aplica a contadorPtr. Cada variable que se declara como
apuntador debe ir precedida por un asterisco (*) Por ejemplo, la declaracin:
float *xPtr, *yPtr;

indica que tanto xPtr como yPtr son apuntadores a valores float. El uso de * en una declaracin
de esta manera indica que la variable que se est declarando es un apuntador. Los apuntadores
pueden declararse para que apunten a objetos de cualquier clase de datos.
Los apuntadores se deben inicializar, ya sea al declararlos o mediante una instruccin de
asignacin. Un apuntador puede inicializarse a 0, a NULL o a una direccin. Un apuntador con
el valor 0 o NULL no apunta a nada. NULL es una constante simblica definida en el archivo de
encabezado <iostream.h> (y en varios archivos de encabezado de la biblioteca estndar) La
iniciacin a NULL de un apuntador es equivalente a inicializarlo a 0, pero en C++ se prefiere 0.
Cuando se asigna 0, se convierte en un apuntador del tipo adecuado. El valor 0 es el nico valor
entero que puede asignarse directamente a una variable de apuntador sin convertir primero
mediante cast el entero a un tipo de apuntador. En la siguiente seccin se estudia la asignacin de
la direccin de una variable a un apuntador.
OPERADORES DE LOS APUNTADORES
El &, u operador de direccin, es un aperador unario que devuelve la direccin de su
operando. Por ejemplo, suponiendo las declaraciones:
int y = 5;
int *yPtr;

la instruccin
yPtr = &y;

APUNTADORES Y CADENA LECCIN 20

20-4

MIGUEL . TOLEDO MARTNEZ

asigna la direccin de la variable y a la variable de apuntador yPtr. Se dice entonces que la


variable yPtr apunta a y. La figura 20.2 muestra una representacin esquemtica de la memoria
tras la ejecucin de la asignacin previa. En la figura mostramos la relacin de apuntador
dibujando una flecha del apuntador hacia el objeto al que apunta.
y
5
yPtr

Figura 20.2. Representacin grfica de un apuntador que apunta a una variable entera en la memoria

La figura 20.3 muestra la representacin del apuntador en la memoria, suponiendo que la


variable entera y est almacenada en la localidad 600000 y que la variable de apuntador yPtr est
almacenada en la localidad 500000. El operando del operador de direccin debe ser un lvalue
(left value) (es decir, algo a lo que puede asignarse un valor, como un nombre de variable); el
operador de direccin no puede aplicarse a constantes, a expresiones que no den como resultado
referencias, ni a variables declaradas con la clase de almacenamiento register.
yPtr
500000

600000

600000

El operador *, conocido comnmente como operador de indireccin u operador de


desreferenciacin, devuelve un sinnimo, alias o apodo del objeto hacia el que apunta su
operando (es decir, su apuntador) Por ejemplo (haciendo referencia a la figura 20.2), la
instruccin:
cout << *yPtr << endl;

imprime el valor de la variable y, es decir 5, de la misma manera que lo hara la instruccin:


cout << y << endl;

El empleo de * de esta manera se conoce como desreferenciacin de un apuntador.


Observe que tambin puede utilizarse un apuntador desreferenciado del lado izquierdo de una
instruccin de asignacin, como:
*yPtr = 9;

que asignar 9 a y en la figura 20.3. El apuntador desreferenciado tambin podra usarse para
recibir un valor de entrada como en el caso de:
cin >> *yPtr;

El apuntador desreferenciado es un lvalue, o valor izquierdo.

APUNTADORES Y CADENA LECCIN 20

20-5

MIGUEL . TOLEDO MARTNEZ

Ejemplo 20.1
El programa siguiente: APUNTADOR1.CPP, muestra los operadores de los apuntadores. En este ejemplo,
las localidades de memoria se envan a la salida como enteros hexadecimales.
/* El siguiente programa: APUNTADOR1.CPP, muestra el empleo de los operadores & y *
*/
#include <iostream.h>

//Para cout y cin

void main(void)
{
int a;
int *aPtr;

//a es un entero
//aPtr es un apuntador a un entero

a = 7;
aPtr = &a;

//aPtr se establece a la direccin de a

cout

<< "La direccin de a es: " << &a


<< "\nEl valor de aPtr es: " << aPtr;

cout

<< "\n\nEl valor de a es: " << a


<< "\nEl valor de *Ptr es: " << *aPtr;

cout

<< "\n\nMostrando que * y & son inversos "


<< "entre s. \n&*aPtr = " << &*aPtr
<< "\n*&aPtr = " << *&aPtr << endl;

}//Fin de main()
Observe que la direccin de a y el valor de aPtr son idnticos en la salida, lo que confirma que la direccin
de a efectivamente est asignada a la variable de apuntador aPtr. Los operadores & y * son inversos entre
ellos cuando se aplican ambos consecutivamente a aPtr en cualquier orden, se imprime el mismo
resultado.

LLAMADO DE FUNCIONES POR REFERENCIA


En C++ hay tres maneras de pasarle argumentos a una funcin: mediante llamada por
valor, mediante llamada por referencia con argumentos de referencia y mediante llamada por
referencia con argumentos de apuntador. En leccin anterior comparamos e hicimos la
distincin entre llamada por valor y llamada por referencia con argumentos de referencia. En esta
leccin nos concentraremos en la llamada por referencia con argumentos de apuntador.
Como antes hemos visto, return puede utilizarse para devolverle al invocador un valor
desde una funcin llamada (o regresar el control desde una funcin llamada sin devolver un
valor) Tambin vimos que se le pueden pasar argumentos a una funcin mediante argumentos de
referencia, permitiendo que la funcin modifique los valores originales de los argumentos (por lo
tanto, se puede devolver ms de un valor desde una funcin), o pasar objetos de datos grandes a
una funcin y evitar la sobrecarga de pasar los objetos mediante llamada por valor (que, claro
est, involucra llevar a cabo una copia del objeto) Los apuntadores, como las referencias,
tambin pueden servir para modificar una o ms variables del invocador o para pasar
apuntadores a objetos de datos grandes, evitando la sobrecarga de pasar los objetos mediante
llamada por valor.

APUNTADORES Y CADENA LECCIN 20

20-6

MIGUEL . TOLEDO MARTNEZ

En C++, los programadores se pueden valer de los apuntadores y del operador de


indireccin para simular llamadas por referencia (de la misma manera que se logran las llamadas
por referencia en C) Al llamar una funcin con argumentos que deben ser modificados, se pasa la
direccin de los argumentos. Por lo general, esto se logra aplicndole el operador de direccin
(&) al nombre de la variable que se habr de modificar. Como hemos visto, los arreglos no se
pasan mediante el operador &, pues el nombre de stos es la localidad inicial del mismo en
memoria (el nombre del arreglo es equivalente a &nombreArreglo[0], es decir, un nombre de
arreglo ya es un apuntador. Cuando se pasa la direccin de una variable a una funcin, se puede
emplear el operador de indireccin (*) en la funcin, creando un sinnimo, alias o apodo del
nombre de la variable con ste, a su vez, es posible modificar el valor de la localidad de
memoria del invocador (si la variable no est declarada como const)
Ejemplo 20.2
Los dos programas siguientes: CUBO1.CPP y CUBO2.CPP, son dos versiones de una funcin que eleva al
cubo un entero: cuboValor() y cuboReferencia()
CUBO1.CPP, pasa la variable numero a la funcin cuboValor() mediante una llamada por valor. La
funcin cuboReferencia() eleva al cubo su argumento y le devuelve el valor a main() mediante una
instruccin return. El nuevo valor se asigna a numero en main() Existe la oportunidad de examinar el
resultado de la llamada de funcin antes de modificar el valor de una variable. Por ejemplo, en este
programa se podra haber almacenado el resultado de cuboValor() en otra variable, examinando su valor y
asignando el resultado a numero tras comprobar que el resultado es razonable.
/* El siguiente programa: CUBO1.CPP, eleva al cubo una variable mediante una llamada
por valor.
*/
#include <iostream.h>

//Para cout y cin

int cuboValor(int);

//Prototipo

void main(void)
{
int numero = 5;
cout << "El valor original del nmero es: " << numero;
numero = cuboValor(numero);
cout << "\nEl nuevo valor del nmero es: " << numero << endl;
}//Fin de main()
int cuboValor(int n)
{
return n * n * n;
}//Fin de cuboValor()

//Eleva al cubo la variable local n

El programa CUBO2.CPP pasa la variable numero mediante una llamada por referencia (se pasa la
direccin de numero) a la funcin cuboReferencia() Esta toma nPtr (que es un apuntador a int) como
argumento. La funcin desreferencia el apuntador y eleva al cubo el valor al que apunta nPtr. Esto cambia
el valor de numero en main()

APUNTADORES Y CADENA LECCIN 20

20-7

MIGUEL . TOLEDO MARTNEZ

/* El siguiente programa: CUBO2.CPP, eleva al cubo una variable mediante una llamada
por referencia, con un argumento de apuntador.
*/
#include <iostream.h>

//Para cout y cin

void cuboReferencia(int *);

//Prototipo

void main(void)
{
int numero = 5;
cout << "El valor original del nmero es: " << numero;
cuboReferencia(&numero);
cout << "\nEl nuevo valor del nmero es: " << numero << endl;
}//Fin de main()
void cuboReferencia(int *nPtr)
{
*nPtr = *nPtr * *nPtr * *nPtr;
}//Fin de cuboReferencia()

//Eleva al cubo el nmero en main()

Una funcin que recibe como argumento una direccin debe definir un parmetro de apuntador para recibir
dicha direccin. Por ejemplo, el encabezado de la funcin cuboReferencia() es:
void cuboReferencia(int *nPtr);
El encabezado de la funcin especifica que cuboReferencia() contiene int * entre parntesis. Como sucede
con los dems tipos de variables, no es necesario incluir los nombres de los apuntadores en los prototipos
de funcin. El compilador ignorar los nombres incluidos con fines de documentacin.
En el encabezado de la funcin y en el prototipo de una funcin que espera como argumento un arreglo de
un solo ndice, puede emplearse la notacin de apuntadores en la lista de parmetros de cuboReferencia()
El compilador no hace ninguna distincin entre una funcin que recibe un apuntador y otra que recibe un
arreglo de un solo ndice. Por supuesto, esto significa que la funcin debe saber cundo est recibiendo un
arreglo y cundo se trata simplemente de una variable sencilla sobre la que debe efectuar una llamada por
referencia. Cuando el compilador encuentra un parmetro de funcin para un arreglo de un solo ndice, de
la forma int b[], lo convierte a la notacin de apuntadores int * const b (que se pronuncia como b es un
apuntador constante a un entero); const se explica en la seccin siguiente (empleo del calificador const con
apuntadores) Ambas formas de declaracin de un parmetro de funcin como arreglo de un solo ndice son
intercambiables.
Las figuras 20.3 y 20.4 son anlisis grficos de los programas CUBO1.CPP y CUBO2.CPP.
Antes de que main() llame a cuboValor():
int main()
{
int numero = 5;

numero
5

int cuboValor(int n)
{
return n * n * n;
}//Fin de cuboValor()

n
indefinido

numero = cuboValor(numero);
}//Fin de main()

APUNTADORES Y CADENA LECCIN 20

20-8

MIGUEL . TOLEDO MARTNEZ

Despus de que cuboValor() recibe la llamada:


int main()
{
int numero = 5;

numero
5

int cuboValor(int n)
{
return n * n * n;
}//Fin de cuboValor()

n
5

numero = cuboValor(numero);
}//Fin de main()
Despus de que cuboValor() eleve al cubo el parmetro n:
int main()
{
int numero = 5;

numero
5

int cuboValor(int n)
{
125
return n * n * n;
}//Fin de cuboValor()

int cuboValor(int n)
{
return n * n * n;
}//Fin de cuboValor()

indefinido

numero = cuboValor(numero);
}//Fin de main()
Despus de que cuboValor() regresa a main():
int main()
numero
{
5
int numero = 5;
125
numero = cuboValor(numero);
}//Fin de main()

indefinido

Despus de que main() completa la asignacin de numero:


int main()
{
int numero = 5;

numero
125

int cuboValor(int n)
{
return n * n * n;
}//Fin de cuboValor()

n
indefinido

numero = cuboValor(numero);
}//Fin de main()
Figura 20.3. Anlisis de una llamada por valor tpica.
Antes de la llamada por referencia a cuboReferencia():

int main()
{
int numero = 5;

numero
5

void cuboReferencia(int *nPtr)


{
nPtr
*nPtr = * nPtr * *nPtr * *nPtr; indefinido
}//Fin de cuboValor()

cuboReferencia(&numero);
}//Fin de main()
Despus de la llamada por referencia a cuboReferencia() y antes de elevar *nPtr al cubo:
int main()
{
int numero = 5;

numero
5

void cuboReferencia(int *nPtr)


{
nPtr
*nPtr = * nPtr * *nPtr * *nPtr;
}//Fin de cuboValor()

cuboReferencia(&numero);
}//Fin de main()
APUNTADORES Y CADENA LECCIN 20

20-9

MIGUEL . TOLEDO MARTNEZ

Despus de elevar *nPtr al cubo:


int main()
{
int numero = 5;

numero
125

void cuboReferencia(int *nPtr)


{
nPtr
*nPtr = * nPtr * *nPtr * *nPtr;
}//Fin de cuboValor()

cuboReferencia(&numero);
}//Fin de main()
Figura 20.4. Anlisis de una llamada por referencia tpica con argumento de apuntador.

EMPLEO DEL CALIFICADOR const CON APUNTADORES


El calificador const le permite al programador informarle al compilador que no se debe
modificar el valor de una variable en particular.
Existen seis posibilidades para utilizar (o no utilizar) const con parmetros de funcin
dos con paso de parmetros mediante llamada por valor y cuatro con paso de parmetros
mediante llamada por referencia. Cmo escoger entre las seis posibilidades? Sea la gua el
principio de menor privilegio. Siempre habr que dar a una funcin el suficiente acceso a la
informacin de sus parmetros para que pueda llevar a cabo la tarea encomendada, pero no ms.
En leccin anterior, explicamos que, cuando se invoca una funcin mediante llamada por
valor, se obtiene una copia del argumento (o argumentos) en la llamada de funcin, la cual es la
que pasa a la funcin. Si en la funcin se modifica la copia, el valor original en el invocador se
conserva inalterado. En muchos casos, un valor pasado a una funcin es modificado para que la
funcin pueda llevar a cabo su tarea. Sin embargo, en algunos casos, el valor no debe ser alterado
en la funcin llamada, an si la funcin llamada slo manipula una copia del valor original.
Considere una funcin que toma como argumentos un arreglo de un solo ndice y su
tamao, y que imprime dicho arreglo. Tal funcin deber recorrer el arreglo utilizando un ciclo y
enviar a la salida, uno por uno, los elementos del mismo. El tamao del arreglo se utiliza en el
cuerpo de la funcin para determinar el ndice ms grande del arreglo y poder terminar el ciclo
cuando se ha terminado la impresin. El tamao del arreglo no cambia en el cuerpo de la
funcin.
Si un valor no cambia (o no debe cambiar) en el cuerpo de una funcin a la cual se pasa,
el parmetro debe ser declarado como const con el fin de asegurar que no sea modificado por
accidente. Si se intenta modificar una valor const, el compilador lo detecta y emite un aviso o un
error, dependiendo del compilador.
Hay cuatro maneras de pasar una apuntador a una funcin: con un apuntador no
constante hacia datos no constantes, mediante un apuntador no constante hacia datos
constantes, por medio de un apuntador constante hacia datos no constantes y utilizando un
apuntador constante hacia datos constantes. Cada combinacin ofrece un nivel distinto de
privilegio de acceso.
El mayor acceso se otorga mediante un apuntador no constante hacia datos no constantes
la informacin puede modificarse a travs del apuntador desreferenciado y dicho apuntador se
puede modificar para que apunte hacia otros datos. La declaracin de apuntadores no constantes
APUNTADORES Y CADENA LECCIN 20

20-10

MIGUEL . TOLEDO MARTNEZ

hacia datos no constante no incluye a const. Tal apuntador puede servir para recibir una cadena
en una funcin que se vale de aritmtica de apuntadores para procesar (y tal vez modificar) uno
por uno los caracteres de la cadena. La funcin convertirMayuscula() en el programa
MAYSCULA.CPP declara el parmetro sPtr (char *sPtr) como apuntador no constante hacia
datos no constantes. La funcin procesa la cadena cadena, carcter por carcter, mediante
aritmtica de apuntadores. Los caracteres entre la a y la z son convertidos a sus letras maysculas
a travs de la funcin toupper(), los dems se conservan iguales. La funcin toupper() toma
como argumento un carcter. Si dicho carcter es una letra minscula, se devuelve la letra
mayscula correspondiente; de otro modo, se devuelve el carcter original. La funcin toupper()
es parte de la biblioteca de manejo de caracteres ctype.h.
Ejemplo 20.3
El siguiente programa: MAYSCULA.CPP, ilustra el uso de un apuntador no constante a datos no
constantes.
/* El siguiente programa: MAYUSCULA.CPP, convierte letras minsculas a maysculas, por
medio de un apuntador no constante hacia datos no constantes.
*/
#include <iostream.h>
#include <ctype.h>

//Para cout y cin


//Para toupper()

void convertirMayuscula(char *);


void main(void)
{
char cadena[] = "caracteres y $32.98";
cout
<< "La cadena de caracteres antes de la conversin es : " << cadena;
convertirMayuscula(cadena);
cout
<< "\nLa cadena de caracteres despus de la conversin es: "
<< cadena << endl;
}//Fin de main()
void convertirMayuscula(char *sPtr)
{
while(*sPtr != '\0')
{
if(*sPtr >= 'a' && *sPtr <= 'z')
*sPtr = toupper(*sPtr);
++sPtr;
}//Fin del while
}//Fin de convertirMayuscula()

//Convierte a maysculas
//Mueve sPtr al siguiente carcter

Un apuntador no constante hacia datos constantes es un apuntador que se puede modificar


para que apunte hacia cualquier elemento de informacin del tipo apropiado, pero los datos a los
que apunta no pueden modificarse por medio de ese apuntador. Tal apuntador podra servir para
recibir como argumento un arreglo en una funcin que procesar cada elemento de dicho arreglo
sin modificar los datos.

APUNTADORES Y CADENA LECCIN 20

20-11

MIGUEL . TOLEDO MARTNEZ

Ejemplo 20.4
La funcin imprimirCaracteres() del programa IMPRIMIR.CPP, declara que los parmetros sPtr son del
tipo const char *. La declaracin se lee de derecha a izquierda como sPtr es un apuntador hacia una
constante de caracteres. El cuerpo de la funcin utiliza una estructura for con la que enva a la salida, uno
por uno, los caracteres de la cadena, hasta que encuentra el carcter nulo. Tras la impresin de un carcter,
se incremente sPtr para apuntar al siguiente carcter de la cadena.
/* El siguiente programa: IMPRIMIR.CPP, imprime una cadena, caracter tras caracter,
mediante un apuntador no constante hacia datos constantes.
*/
#include <iostream.h>

//Para cout y cin

void imprimirCaracteres(const char *);


void main(void)
{
char cadena[] = "imprime caracteres de una cadena";
cout
<< "La cadena es:\n";
imprimirCaracteres(cadena);
cout << endl;
}//Fin de main()
/* En imprimirCaracteres(), sPtr es el apuntador hacia una constante de caracteres.
Los caracteres no pueden modificarse por medio de sPtr(es decir, sPtr es un apuntador
de solo lectura).
*/
void imprimirCaracteres(const char *sPtr)
{
for(; *sPtr != '\0'; sPtr++)
// No hay inicializacin
cout << *sPtr;
}//Fin de imprimirCaracteres()

Ejemplo 20.5
El siguiente programa: MODIFICAR.CPP, muestra los mensajes de error que se generan al intentar
compilar una funcin que recibe un apuntador hacia datos constantes y luego intenta emplear dicho
apuntador para modificar los datos.
/* El siguiente programa: MODIFICAR.CPP, intenta modificar la informacin por medio
de un apuntador no constante hacia datos constantes.
*/
#include <iostream.h>

//Para cout y cin

void f(const int *);


void main(void)
{
int y;
f(&y);
}//Fin de main()

APUNTADORES Y CADENA LECCIN 20

//f intenta una modificacin ilegal

20-12

MIGUEL . TOLEDO MARTNEZ

// En f, xPtr es un apuntador hacia una constante entera


void f(const int *xPtr)
{
*xPtr = 100; // No se puede modificar un objeto const
}//Fin de f()
Los mensajes que el compilador enva son los siguientes:
Cannot modify a const object
Parameter xPtr is never used

Como sabemos, los arreglos son tipos de datos agrupados que almacenan elementos de
datos relacionados del mismo tipo bajo un mismo nombre. En otra leccin se estudiar otra
forma de tipo de datos agrupados, llamado estructura (a veces conocido como registro en otros
lenguajes) Una estructura es capaz de almacenar elementos de datos relacionados de distintos
tipos bajo un mismo nombre (por ejemplo, para almacenar informacin sobre todos los
empleados de una compaa) Al invocar una funcin con un arreglo como argumento, ste se
pasa de manera automtica a la funcin simulando una llamada por referencia. Sin embargo, las
estructuras siempre se pasan mediante llamada por valor se pasa una copia de toda la estructura.
Esto requiere la sobrecarga en tiempo de ejecucin que es causada por copiar todos los elementos
de datos de la estructura y almacenarlos en la pila de llamadas de funcin de la computadora
(que es el lugar donde se almacenan las variables locales empleadas en la funcin mientras sta
se ejecuta) Cuando hay que pasar una estructura a una funcin, puede utilizarse un apuntador
hacia datos constantes (o una referencia hacia datos constantes), logrando el desempeo de una
llamada por referencia y la proteccin de una llamada por valor. Cuando el apuntador se pasa
hacia una estructura, slo hay que copiar la direccin en la que se almacena tal estructura. En una
mquina con direcciones de 4 bytes, se hace una copia de 4 bytes de memoria, en lugar de los, tal
vez, cientos o miles de bytes de la estructura.
Un apuntador constante hacia datos no constantes es un apuntador que siempre apunta a
la misma localidad de memoria; los datos de dicha localidad se pueden modificar a travs del
apuntador. Esto es lo predeterminada para un nombre de arreglo, el cual es un apuntador
constante hacia el inicio del arreglo. Es posible acceder y cambiar toda la informacin del arreglo
por medio de su nombre y sus ndices. Un apuntador constante hacia datos no constantes puede
servir para recibir como argumento un arreglo en una funcin que accede a elementos del mismo,
empleado slo notacin de ndices. Los apuntadores que se declaran como const deben ser
inicializados cuando se declaran (si el apuntador es un parmetro de funcin, se inicializa con un
apuntador que se pasa a la funcin)
Ejemplo 20.6
El programa MODIFICAR2.CPP, intenta modificar un apuntador constante. El apuntador ptr se declara como de
tipo int * const. Esta declaracin se lee de derecha a izquierda como ptr es un apuntador constante hacia un
entero. El apuntador se inicializa con la direccin de la variable entera x. El programa intenta asignarle a ptr la
direccin de y, lo cual genera un mensaje de error. Observe que al asignarle el valor 7 a *ptr no se produce un error
el valor al que apunta ptr sigue siendo modificable.

APUNTADORES Y CADENA LECCIN 20

20-13

MIGUEL . TOLEDO MARTNEZ

/* El siguiente programa: MODIFICAR2.CPP, intenta modificar un apuntador constante hacia


datos no constantes.
*/
#include <iostream.h>

//Para cout y cin

void main(void)
{
int x, y;
/* ptr es un apuntador constante hacia un entero. Se puede modificar un entero
a travs de ptr, pero ptr siempre apunta a la misma localidad de memoria.
*/
int *const ptr = &x;
*ptr = 7;
ptr = &y;
}//Fin de main()
Los mensajes que enva el compilador son los siguientes:
Cannot modify a const object
y is declared but never used

El menor privilegio de acceso se otorga mediante un apuntador constante hacia datos


constantes. Tal apuntador siempre apunta a la misma localidad de memoria y los datos de dicha
localidad no pueden ser modificados. Esta es la manera en que debe pasarse un arreglo a una
funcin que slo consulta dicho arreglo por medio de notacin de ndices y que no lo modifica.
Ejemplo 20.7
El programa MODIFICAR3.CPP, declara la variable de apuntador ptr como de tipo const int *const. Esta
declaracin se lee de derecha a izquierda como ptr es un apuntador constante hacia un entero constante.
El programa muestra los mensajes de error generados al intentar modificar los datos hacia los que apunta
ptr y al intentar alterar la direccin almacenada en la variable de apuntador. Observe que al intentar enviar a
la salida el valor al que apunta ptr no se genera un error, pues en la instruccin de salida no se est
modificando nada.
/* El siguiente programa: MODIFICAR3.CPP, intenta modificar un apuntador constante hacia
datos constantes.
*/
#include <iostream.h>

//Para cout y cin

void main(void)
{
int x = 5, y;
/
/* ptr es un apuntador constante hacia un entero constante. ptr siempre apunta
a la misma localidad y el entero de dicha localidad no puede ser modificado.
*/
const int *const ptr = &x;
cout << *ptr << endl;
*ptr = 7;
ptr = &y;
}//Fin de main()
APUNTADORES Y CADENA LECCIN 20

20-14

MIGUEL . TOLEDO MARTNEZ

ORDENAMIENTO DE burbuja MEDIANTE LLAMADA POR REFERENCIA


Ejemplo 20.8
El programa BURBUJA1.CPP, de la leccin 18 se modificar para que utilice dos funciones
clasificBurbuja() e intercambiar() (vase el programa BURBUJA.CPP) La funcin clasificBurbuja() se
encarga de ordenar el arreglo. Llama a la funcin intercambiar() para que intercambie los elementos del
arreglo arreglo[j] y arreglo[j + 1] Recuerde que C++ aplica el ocultamiento de informacin entre
funciones, por lo que intercambiar() no tiene acceso a los elementos individuales del arreglo
clasificBurbuja() Debido a que clasificBurbuja() quiere que intercambiar() tenga acceso a los elementos
del arreglo que se intercambiarn, pasa cada uno de ellos a intercambiar() mediante una llamada por
referencia; la direccin de cada elemento del arreglo se pasa explcitamente. Aunque los arreglos completos
se pasan automticamente mediante llamada por referencia, los elementos individuales del arreglo son
escalares y por lo general se pasan mediante una llamada por valor. As que clasificBurbuja() utiliza el
operador de direccin (&) en cada elemento del arreglo en la llamada de intercambiar(), como sigue:
Intercambiar(&arreglo[j], &arreglo[j + 1]);
Poniendo en prctica la llamada por referencia. La funcin intercambiar() recibe &arreglo[j] en la variable
de apuntador elemento1Ptr. Gracias al ocultamiento de informacin, intercambiar() no puede saber el
nombre arreglo[j], pero puede utilizar *elemento1Ptr como sinnimo de arreglo[j] Por lo tanto, cuando
intercambiar() hace referencia a *elemento1Ptr, de hecho est referenciando a arreglo[j] en
clasificBurbuja() De igual manera, cuando intercambiar() hace referencia a *elemento2Ptr, en realidad
est referenciando a arreglo[j + 1] en clasificBurbuja() Aun cuando no se permite que intercambiar() diga:
temporal = arreglo[j];
arreglo[j] = arreglo[j + 1];
arreglo[j + 1] = temporal;
se logra exactamente el mismo efecto mediante:

en la funcin intercambiar del programa BURBUJA.CPP


/* El siguiente programa: BURBUJA.CPP, pone los valores en un arreglo, los ordena en
orden ascendente e imprime el arreglo resultante.
*/
#include <iostream.h>
#include <iomanip.h>

//Para cout y cin


//Para setw()

void clasificBurbuja(int *, const int);


void main(void)
{
const int TAMANO_ARREGLO = 10;
int arreglo[TAMANO_ARREGLO] = {2, 6, 4, 8, 10, 12, 89, 68, 45, 37};
int i;
cout << "Datos en el orden original\n";
for(i = 0; i < TAMANO_ARREGLO; i++)
cout << setw(4) << arreglo[i];
clasificBurbuja(arreglo, TAMANO_ARREGLO); //Ordena el arreglo
cout << "\nDatos en orden ascendente\n";
APUNTADORES Y CADENA LECCIN 20

20-15

MIGUEL . TOLEDO MARTNEZ

for(i = 0; i < TAMANO_ARREGLO; i++)


cout << setw(4) << arreglo[i];
cout << endl;
}//Fin de main()
void clasificBurbuja(int *arre, const int tamano)
{
void intercambiar(int *, int *);
for(int pasada = 0; pasada < tamano - 1; pasada++)
for(int j = 0; j < tamano -1; j++)
if(arre[j] > arre[j+1])
intercambiar(&arre[j], &arre[j+1]);
}//Fin de clasificBurbuja()
void intercambiar(int *elemento1Ptr, int *elemento2Ptr)
{
int temporal
= *elemento1Ptr;
*elemento1Ptr = *elemento2Ptr;
*elemento2Ptr = temporal;
}//Fin de intercambiar()
Deben observarse varias caractersticas de la funcin clasificBurbuja() El encabezado de la funcin declara
arreglo como int *arre, en lugar de como int arre[], indicando que clasificBurbuja() recibe como
argumento un arreglo de un solo ndice (nuevamente, estas notaciones son intercambiables) El parmetro
tamano se declara como const, aplicando el principio de menor privilegio. Aunque el parmetro tamano
recibe una copia de un valor de main() y la modificacin de la copia no puede cambiar el valor en main()
clasificBurbuja() no necesita alterar tamano para que cumpla su tarea. El tamao del arreglo permanece
fijo durante la ejecucin de clasificBurbuja() Por lo tanto, tamano se declara como const para asegurarse
de que no se pueda modificar. Si el tamao del arreglo se modificara durante el proceso de ordenamiento, el
algoritmo de ordenamiento no se ejecutara correctamente.
El prototipo de la funcin intercambiar() est incluido en el cuerpo de la funcin clasificBurbuja() porque
es la nica funcin que llama a intercambiar() La colocacin del prototipo en clasificBurbuja() restringe
las llamadas correctas a intercambiar() a aquellas que se llevan a cabo desde clasificBurbuja() Otras
funciones que intentan llamar a intercambiar() no tienen acceso a un prototipo de funcin apropiado. Por lo
general, ste es un error de sintaxis, pues C++ requiere prototipos de funcin.
Observe que la funcin clasificBurbuja() recibe como parmetro el tamao del arreglo. Dicha funcin debe
saber el tamao del arreglo para poder ordenarlo. Cuando se pasa un arreglo a una funcin, sta recibe la
direccin de memoria del primer elemento del arreglo. El tamao del arreglo se debe pasar por separado.
Al definir la funcin clasificBurbuja() para que reciba como parmetro el tamao del arreglo, se logra que
cualquier programa que ordene arreglos de enteros de un solo ndice de tamao arbitrario utilice dicha
funcin.
El tamao del arreglo podra haberse programado directamente en la funcin. Esto limita el uso de la
funcin a un arreglo de un tamao especfico y reduce su capacidad de reutilizacin. Slo los programas
que procesen arreglos de enteros de un solo ndice y que sean del tamao especificado podrn utilizar la
funcin.

Ejemplo 20.9
C++ ofrece el operador unario sizeof, el cual determina el tamao en bytes de un arreglo (o de cualquier
otro tipo de datos) durante la compilacin del programa. Cuando al nombre de un arreglo se le aplica el
operador sizeof, como en el programa SIZEOF.CPP, ste devuelve la cantidad total de bytes que hay en el
APUNTADORES Y CADENA LECCIN 20

20-16

MIGUEL . TOLEDO MARTNEZ

arreglo como un valor de tipo size_t, que generalmente es un unsigned int. La computadora empleada aqu
almacena las variables de tipo float en 4 bytes de memoria, y arreglo se ha declarado como de 20
elementos, por lo que arreglo ocupa 80 bytes de memoria. Al aplicarle el operador sizeof a un parmetro de
apuntador de una funcin que recibe un arreglo como argumento, dicho operador devuelve el tamao en
bytes (4) del apuntador, no el tamao del arreglo.
/* El siguiente programa: SIZEOF.CPP, utiliza el operador sizeof() aplicado a un nombre
de arreglo, devuelve la cantidad de bytes del arreglo.
*/
#include <iostream.h>

//Para cout y cin

size_t obtenerTamano(float *);


void main(void)
{
float arreglo[20];
cout

<< "El nmero de bytes en el arreglo es "


<< sizeof(arreglo)
<< "\nEl nmero de bytes devuelto por obtenerTamano() es "
<< obtenerTamano(arreglo) << endl;

}//Fin de main()
size_t obtenerTamano(float *ptr)
{
return sizeof(ptr);
}//fin de obtenerTamano()
El nmero de elementos de un arreglo tambin puede determinarse por medio de los resultados de dos
operaciones de sizeof. Por ejemplo, considere la siguiente declaracin de arreglo:
double arregloReal[22];
Si las variables del tipo de datos double se almacenan en 8 bytes de memoria, el arreglo arregloReal
contiene un total de 176 bytes. Para determinar el nmero de elementos del arreglo, puede utilizarse la
siguiente expresin:
sizeof (arregloReal) / sizeof(double);
La expresin determina el nmero de bytes en el arreglo arregloReal y divide este valor entre el nmero de
bytes que se utiliza en memoria para almacenar un valor double.

Ejemplo 20.10
El programa SIZEOF2.CPP se vale del operador sizeof para calcular el nmero de bytes utilizados para
almacenar cada uno de los tipos de datos estndar de la computadora particular que se est empleado.
/* El siguiente programa: SIZEOF2.CPP, muestra el uso del operador sizeof */
#include <iostream.h>
#include <iomanip.h>

APUNTADORES Y CADENA LECCIN 20

//Para cout y cin


//Para setw()

20-17

MIGUEL . TOLEDO MARTNEZ

void main(void)
{
char c;
short s;
int i;
long l;
float f;
double d;
long double ld;
int arreglo[20], *ptr = arreglo;
cout

<< "sizeof c = " << sizeof c


<< "\tsizeof(char) = " << sizeof(char)
<< "\nsizeof s = " << sizeof s
<< "\tsizeof(short) = " << sizeof(short)
<< "\nsizeof i = " << sizeof i
<< "\tsizeof(int) = " << sizeof(int)
<< "\nsizeof l = " << sizeof l
<< "\tsizeof(long) = " << sizeof(long)
<< "\nsizeof f = " << sizeof f
<< "\tsizeof(float) = " << sizeof(float)
<< "\nsizeof d = " << sizeof d
<< "\tsizeof(double) = " << sizeof(double)
<< "\nsizeof ld = " << sizeof ld
<< "\tsizeof(long double) = " << sizeof(long double)
<< "\nsizeof arreglo = " << sizeof arreglo
<< "\nsizeof ptr = " << sizeof ptr
<< endl;

}//Fin de main()
El operador sizeof se puede aplicar a cualquier nombre de variable, nombre de tipo o valor constante.
Cuando se aplica a un nombre de variable (que no sea un nombre de arreglo) o valor constante, se devuelve
el nmero de bytes que se emplean para almacenar el tipo de variable o constante especficos. Observe que
los parntesis que se utilizan con sizeof son obligatorios si se indica como operando un nombre de tipo; no
se requieren si se indica como operando un nombre de variable. Es importante recordar que sizeof es un
operador, no una funcin.

EXPRESIONES DE APUNTADORES Y ARITMTICA DE APUNTADORES


Los apuntadores son operandos vlidos en las expresiones aritmticas, las expresiones de
asignacin y las expresiones de comparacin. Sin embargo, no todos los operadores que se
utilizan normalmente en estas expresiones son vlidos con variables de apuntador. Esta seccin
describe los operadores que pueden tener apuntadores como sus operandos, adems de la manera
en que se utilizan.
Es posible llevar a cabo un conjunto limitado de operaciones aritmticas sobre los
apuntadores. Un apuntador puede incrementarse (++) o decrementarse (--), adems es posible
sumarle un entero a un apuntador (+ o +=), restar un entero de un apuntador (- o -=) y restar un
apuntador de otro.
Suponga que se ha declarado un arreglo int v[5] y que su primer elemento est en la
localidad 3000 de la memoria. Adems, suponga que se ha inicializado el apuntador vPtr para
que apunte a v[0], es decir, el valor de vPtr es 3000. La figura 20.5 es un diagrama de dicha
APUNTADORES Y CADENA LECCIN 20

20-18

MIGUEL . TOLEDO MARTNEZ

situacin en una mquina con enteros de 4 bytes. Observe que vPtr puede inicializarse para que
apunte al arreglo v mediante cualquiera de las siguientes instrucciones:
vPtr = v;
vPtr = &v[0];
Localidad

3000

3004

v[0]

3008

v[1]

3012

v[2]

3016

v[3]

v[4]

Variable de apuntador vPtr


Figura 20.5. El arreglo v y la variable de apuntador vPtr que apunta a v.

En la aritmtica convencional, la suma de 3000 + 2 da el valor 3002. Normalmente ste


no es el caso con la aritmtica de apuntadores. Cuando a un apuntador se le suma o resta un
entero, dicho apuntador no se incrementa o decrementa en la misma cantidad que el entero, sino
en el entero por el tamao del objeto hacia el que apunta el apuntador. El nmero de bytes
depende del tipo de datos del objeto. Por ejemplo, la instruccin
vPtr += 2;

producir 3008 (3000 + 2 * 4), suponiendo que los enteros se almacenan en 4 bytes de memoria.
En el arreglo v, vPtr ahora apuntar a v[2] (vea la figura 20.6)
Localidad
3000

3004

v[0]

v[1]

3008
v[2]

3012
v[3]

3016
v[4]

Variable de apuntador vPtr


Figura 20.6. El apuntador vPtr despus de la aritmtica de apuntadores.

Si se almacena un entero en 2 bytes de memoria, entonces el clculo anterior dar la


localidad de memoria 3004 (3000 + 2 * 2) Si el arreglo fuera de un tipo de datos distinto, la
instruccin previa incrementara el apuntador por el doble del nmero de bytes necesarios para
almacenar un objeto de dicho tipo de datos. Al efectuar aritmtica de apuntadores sobre un
APUNTADORES Y CADENA LECCIN 20

20-19

MIGUEL . TOLEDO MARTNEZ

arreglo de caracteres, los resultados sern consistentes con la aritmtica comn, pues cada
carcter tiene un byte de longitud.
Si vPtr se hubiera incrementado a 3016, que apunta a v[4], la instruccin
vPtr -= 4;

establecera vPtr nuevamente a 3000 el comienzo del arreglo. Si se est incrementando o


decrementando en uno un apuntador, es posible utilizar los operadores de incremento (++) y
decremento (--)
Las instrucciones
++vPtr;
vPtr++;

incrementan el apuntador para que apunte a la siguiente localidad del arreglo. Las instrucciones
--vPtr;
vPtr--;

decrementan el apuntador para que apunte al elemento previo del arreglo.


Las variables de apuntador pueden restarse entre ellas. Por ejemplo, si vPtr contiene la
localidad 3000 y v2Ptr la direccin 3008, la instruccin
x = v2Ptr vPtr;

le asignar a x el nmero de elementos del arreglo de vPtr a v2Ptr, que es 2 en este caso. La
aritmtica de apuntadores carece de significado a menos que se lleve a cabo sobre un arreglo. No
podemos suponer que dos variables del mismo tipo estn almacenadas de manera contigua en la
memoria, a menos que sean elementos adyacentes de un arreglo.
Es posible asignar un apuntador a otro, si ambos son del mismo tipo. De otro modo, es
necesario emplear un operador de conversin mediante cast para convertir el valor del apuntador
del lado derecho de la asignacin al tipo de apuntador a la izquierda de la asignacin. La
excepcin a esta regla es el apuntador a void (es decir, void *), que es un apuntador genrico
capaz de representar cualquier tipo de apuntador. Todos los tipos de apuntadores pueden ser
asignados a void sin necesidad de una conversin mediante cast. Sin embargo, un apuntador a
void no puede asignarse directamente a un apuntador de otro tipo; primero hay que convertir el
apuntador void al tipo de apuntador adecuado.
No es posible desreferenciar un apuntador void *. Por ejemplo, el compilador sabe que un
apuntador a int hace referencia a cuatro bytes de memoria en una mquina con enteros de 4
bytes, pero los apuntadores a void simplemente contienen localidades de memoria para un tipo
de datos desconocido; por lo tanto, el compilador no sabe el nmero preciso de bytes a los que
hace referencia el apuntador. El compilador debe saber el tipo de datos para determinar el
nmero de bytes a desreferenciar para un apuntador en particular. En el caso de un apuntador a
void, esta cantidad de bytes no puede determinarse a partir del tipo.

APUNTADORES Y CADENA LECCIN 20

20-20

MIGUEL . TOLEDO MARTNEZ

Los apuntadores se pueden comparar por medio de operadores de igualdad y relacionales,


pero tales comparaciones no tienen significado a menos que los apuntadores apunten a miembros
del mismo arreglo. Las comparaciones de apuntadores comparan las direcciones almacenadas en
los apuntadores. Una comparacin de dos apuntadores que apuntan al mismo arreglo podra
mostrar, por ejemplo, que un apuntador apunta a un elemento de ndice mayor que el otro
apuntador. Un uso comn de la comparacin de apuntadores es la determinacin de si un
apuntador es 0.
RELACIN ENTRE APUNTADORES Y ARREGLOS
Los arreglos y los apuntadores estn relacionados ntimamente en C++ y se pueden
utilizar de manera casi intercambiable. Es posible pensar en los nombres de arreglos como si
fueran apuntadores constantes. Los apuntadores pueden servir para realizar cualquier operacin
en la que intervengan ndices de arreglos.
Suponga que se han declarado tanto el arreglo de entero b[5] como la variable entera de
apuntador bPtr. Debido a que el nombre del arreglo (sin ndice) es un apuntador al primer
elemento del mismo, podemos establecer bPtr a la direccin del primer elemento del arreglo b
por medio de la instruccin
bPtr = b;

Esto es equivalente a tomar la direccin del primer elemento del arreglo, como sigue
bPtr = &b[0];

El elemento b[3] del arreglo se puede referencia de manera alterna con la expresin de
apuntador
*(bPtr + 3)

El 3 de la expresin previa es el desplazamiento al apuntador. Cuando el apuntador


apunta al inicio de un arreglo, el desplazamiento indica el elemento del arreglo al que se har
referencia y el valor de dicho desplazamiento es idntico al ndice del arreglo. La notacin previa
se conoce como notacin de apuntador y desplazamiento. Los parntesis son necesarios porque
la precedencia de * es mayor que la de +. Sin stos, la expresin anterior sumara 3 al valor de la
expresin *bPtr (es decir, se sumara 3 a b[0]), suponiendo que bPtr apuntara al inicio del
arreglo. As como el elemento del arreglo se puede referenciar con una expresin de apuntador,
la direccin
&b[3]

se puede escribir con la expresin de apuntador


bPtr + 3

El arreglo mismo puede ser tratado como apuntador y utilizado con aritmtica de
apuntadores. Por ejemplo, la expresin
*(b + 3)

APUNTADORES Y CADENA LECCIN 20

20-21

MIGUEL . TOLEDO MARTNEZ

tambin se refiere al elemento del arreglo b[3]. En general, todas las expresiones de arreglos con
ndices se pueden escribir con un apuntador y un desplazamiento. En este caso, se utiliz
notacin de apuntador y desplazamiento con el nombre del arreglo como apuntador. Observe que
la instruccin previa no modifica de ninguna manera el nombre del arreglo; b an apunta al
primer elemento de la misma.
Los puntadores se pueden indexar de la misma manera que los arreglos. Por ejemplo, la
expresin
bPtr[1]

hace referencia al elemento b[1] del arreglo; esta expresin se conoce como notacin de
apuntador e ndice.
Recuerde que, en esencia, los nombres de arreglos son apuntadores constantes; siempre
apuntan al inicio del arreglo. Por lo tanto, la expresin
b += 3

es invlida debido a que intenta modificar el valor del nombre del arreglo por medio de
aritmtica de apuntadores.
Ejemplo 20.11
El programa APUNTADORES.CPP, utiliza los cuatro mtodos que hemos explicado para hacer referencia
a los elementos de un arreglo (indizacin de arreglos, apuntador y desplazamiento con el nombre del
arreglo como apuntador, indizacin de apuntadores y apuntador y desplazamiento con un apuntador)
para imprimir los cuatro elementos del arreglo de enteros b.
/* El siguiente programa: APUNTADORES.CPP, utiliza las notaciones de ndices y por apuntadores
con arreglos.
*/
#include <iostream.h>

//Para cout y cin

void main(void)
{
int b[] = {10, 20, 30, 40};
int *bPtr = b;
//Establece bPtr para que apunte al arreglo b
cout

<< "Arreglo b impreso con:\n"


<< "Notacin de ndices\n";

for(int i = 0; i < 4; i++)


cout << "b[" << i << "] = " << b[i] << '\n';
cout

<< "\nNotacin apuntador/desplazamiento en donde\n"


<< "el apuntador es el nombre del arreglo\n";

for(int desplazamiento = 0; desplazamiento < 4; desplazamiento++)


cout
<< "*(b + " << desplazamiento << ") = "
<< *(b + desplazamiento) << '\n';

APUNTADORES Y CADENA LECCIN 20

20-22

MIGUEL . TOLEDO MARTNEZ

cout << "\nNotacion de ndices de apuntador\n";


for(int i = 0; i < 4; i++)
cout << "bPtr[" << i << "] = " << bPtr[i] << '\n';
cout

<< "\nNotacin apuntador/desplazamiento\n";

for(int desplazamiento = 0; desplazamiento < 4; desplazamiento++)


cout
<< "*(bPtr + " << desplazamiento << ") = "
<< *(bPtr + desplazamiento) << '\n';
}//Fin de main()

Ejemplo 20.12
Para ilustrar an ms que los arreglos y los apuntadores son intercambiables, vea las dos funciones de copia
de cadenas (copia1() y copia2()) del programa APUNTADORES2.CPP. Ambas funciones copian una
cadena a un arreglo de caracteres. Tras una comparacin de los prototipos de funcin de copia1() y
copia2(), ambas funciones parecen idnticas (gracias a que los arreglos y los apuntadores son
intercambiables) Estas funciones llevan a cabo la misma tarea, pero se implementan de manera diferente.
/* El siguiente programa: APUNTADORES2.CPP, copia una cadena por medio de notacin de
arreglos y notacin de apuntadores.
*/
#include <iostream.h>

//Para cout y cin

void copia1(char *, const char *);


void copia2(char *, const char *);
void main(void)
{
char
cadena1[10], *cadena2 = "Hola",
cadena3[10], cadena4[] = "Adis";
copia1(cadena1, cadena2);
cout << "cadena1 = " << cadena1 << endl;
copia2(cadena3, cadena4);
cout << "cadena3 = " << cadena3 << endl;
}//Fin de main()
//Copia s2 a s1 mediante notacin de arreglos
void copia1(char *s1, const char *s2)
{
for(int i = 0; (s1[i] = s2[i]) != '\0'; i++)
;
//El cuerpo no hace nada
}//Fin de copia1()
//Copia s2 a s1 mediante notacin de apuntadores
void copia2(char *s1, const char *s2)
{
for(; (*s1 = *s2) != '\0'; s1++, s2++)
;
//El cuerpo no hace nada
}//Fin de copia2()

APUNTADORES Y CADENA LECCIN 20

20-23

MIGUEL . TOLEDO MARTNEZ

La funcin copia1() utiliza notacin de ndices de arreglo para copiar la cadena s2 al arreglo de caracteres
s1. Adems, declara una variable entera i, que funciona como contador, la cual ser el ndice del arreglo. El
encabezado de la estructura for realiza la operacin completa de copia su cuerpo es la instruccin vaca. El
encabezado especifica que i se inicializa a cero y se incrementa en uno con cada iteracin del ciclo. La
condicin for, (s1[i] = s2[i]) != \0, realiza la operacin de copia, carcter por carcter, de s2 a s1. Al llegar
al carcter nulo en s2, ste es asignado a s1 y termina el ciclo, pues el carcter nulo es igual a \0. Recuerde
que el valor de una instruccin de asignacin el valor asignado al argumento de la izquierda.
La funcin copia2() se vale de apuntadores y aritmtica de apuntadores para copiar la cadena de s2 al
arreglo de caracteres s1. Otra vez, el encabezado de la estructura for efecta toda la operacin de copia.
Dicho encabezado no incluye ninguna iniciacin de variables. Como en la funcin copia1(), la condicin
(*s1 = *s2) != \0 realiza la operacin de copia. El apuntador s2 es desreferenciado y el carcter resultante
es asignado al apuntador desreferenciado s1. Tras la asignacin que sucede en la condicin, se incrementan
los apuntadores para que apunten al siguiente elemento del arreglo s1 y al siguiente carcter de la cadena
s2, respectivamente. Al encontrar el carcter nulo en s2, se le asigna al apuntador desreferenciado s1 y
termina el ciclo.
Observe que el primer argumento, tanto de copia1() como de copia2(), debe ser un arreglo lo bastante
grande para contener la cadena del segundo argumento. De otra manera, puede suceder un error cuando se
intente escribir en una localidad de memoria fuera de los lmites del arreglo. Adems observe que el
segundo parmetro de cada funcin se declara como const char * (cadena constante) En ambas funciones
se copia el segundo argumento al primero los caracteres se copian del segundo argumento, uno a la vez,
pero nunca se modifican. Por lo tanto, se declara el segundo parmetro de modo que apunte a un valor
constante, aplicando as el principio de menor privilegio. Ninguna de las dos funciones necesita modificar
el segundo argumento, por lo tanto no tienen la capacidad de hacerlo.

ARREGLOS DE APUNTADORES
Los arreglos pueden contener apuntadores. Un uso comn de tal estructura de datos es
para formar arreglos de cadenas. Cada entrada del arreglo es una cadena, pero en C++ una
cadena es, en esencia, un apuntador a su primer carcter. Por lo tanto, cada entrada de un arreglo
de cadenas es, de hecho, un apuntador al primer carcter de una cadena. Considere la declaracin
del arreglo de cadena palos, que podra ser til para representar una baraja de naipes.
char *palos[4] = {Corazones, Diamantes, Trboles, Espadas};

La parte palos[4] de la declaracin indica un arreglo de 4 elementos. La parte char *


indica que cada elemento del arreglo palos es del tipo apuntador a char. Los cuatro valores a
poner en el arreglo son los palos de la baraja, Corazones, Diamantes, Trboles y
Espadas. Cada uno de ellos se almacena en memoria como una cadena de caracteres que
termina con el carcter nulo y cuya longitud es de un carcter ms que el nmero de caracteres
que hay entre comillas. Las cadenas son de 10, 10, 9 y 8 caracteres de longitud, respectivamente.
Aunque parece que estas cadenas se ponen en el arreglo palos, de hecho slo se almacenan
apuntadores (vea la figura 20.7)-cada apuntador apunta al primer carcter de su cadena
correspondiente. Por lo tanto, aun cuando el arreglo palos es de tamao fijo, da acceso a cadenas
de caracteres de cualquier longitud. Esta flexibilidad es un ejemplo de las poderosas
posibilidades de estructuracin de datos que tiene C++.
Las cadenas de los distintos palos de la baraja podran ponerse en un arreglo de doble
ndice en el que cada fila representa un palo y cada columna una de las letras del nombre del
palo. Tal estructura de datos debe tener un nmero fijo de columnas por fila, que tiene que ser tan
grande como la cadena ms larga. Por lo tanto, se desperdiciara una cantidad importante de
memoria si se almacenara un gran nmero de cadenas cuando la mayora de ellas fuera ms corta
APUNTADORES Y CADENA LECCIN 20

20-24

MIGUEL . TOLEDO MARTNEZ

que la cadena ms larga. Para ayudar a representar una baraja, en la siguiente seccin se emplean
arreglos de cadenas.
palos[0]

\0

palos[0]

\0

palos[0]

\0

palos[0]

\0

Figura 20.7. Representacin grfica del arreglo palos.

CASO DE ESTUDIO: Simulacin de barajado y reparticin de naipes


En esta seccin utilizamos la generacin aleatoria de nmeros para desarrollar un
programa de simulacin de barajado y reparticin de naipes. Este programa puede utilizarse
despus para implementar programas de distintos juegos de baraja. Para relevar algunos sutiles
problemas de desempeo, hemos utilizado intencionalmente algoritmos de barajado y reparticin
subptimos. En los ejercicios desarrollaremos algoritmos ms eficientes.
Mediante el enfoque de refinacin descendente paso a paso, se desarrolla un programa
que baraja 52 naipes y luego los reparte. Dicho enfoque es especialmente til cuando se abordan
problemas mayores y ms complejos de los que hemos visto en las primeras lecciones.
Para representar la baraja, utilizamos un arreglo paquete de doble ndice, de 4 por 13 (vea
la figura 20.8) Las filas corresponden a los palos: la fila 0 a los corazones, la fila 1 a los
diamantes, la 2 a los trboles y la 3 a las espadas. Las columnas corresponden a los valores de
los naipes: las columnas 0 a 9 corresponden del as al 10, respectivamente, y las columnas de la
10 a la 12 corresponden a la sota, la reina y el rey. Cargaremos el arreglo de cadenas palos con
las cadenas de caracteres que representan los cuatro palos y el arreglo de cadena cara con las
cadenas de caracteres que corresponden a los trece valores de los naipes.
0

10

11

12

Corazones 0
Diamantes 1
Trboles 2
Espadas 3

Figura 20.8. Arreglo (paquete) de doble ndice que representa una baraja.

APUNTADORES Y CADENA LECCIN 20

20-25

MIGUEL . TOLEDO MARTNEZ

En la figura 20.8 cada columna (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) representan: As,
Dos, Tres, Cuatro, Cinco, Seis, Siete, Ocho, Nueve, Diez, Sota, Reina y Rey respectivamente.
As paquete[2][12] (parte sombreada de la figura 20.8) representa al rey de trboles.
Trboles

Rey

Esta baraja simulada puede barajarse como sigue. Primero, el arreglo paquete se pone
en ceros. Luego se seleccionan al azar una fila (rengln, 0 a 3) y una columna (columna, 0 a 12)
El nmero 1 se inserta en el elemento del arreglo paquete[rengln][columna], indicando que
este naipe va a ser el primero de la baraja en ser repartido. Este proceso continua con la insercin
aleatoria de los nmeros 2, 3, ..., 52 en el arreglo paquete, con lo que se indica el segundo,
tercero, ... y quincuagsimo segundo naipe a repartir. A medida que se va llenando el arreglo
paquete con nmeros de naipe, es posible que una carta se seleccione dos veces, es decir, que
paquete[rengln][columna] sea distinto de cero cuando sea seleccionado. Esta seleccin
simplemente se ignora y se seleccionan al azar otro rengln y otra columna hasta dar con un
naipe no seleccionado. Tarde o temprano los nmeros del 1 al 52 ocuparn los 52 espacios del
arreglo paquete. En este momento se termina el barajado de los naipes.
Este algoritmo de barajado podra ejecutarse durante un tiempo indefinidamente grande
si se seleccionan repetidamente al azar naipes que ya han sido barajados. Este fenmeno se
conoce como aplazamiento indefinido. En los ejercicios se estudiar un mejor algoritmo de
barajado que elimina la posibilidad de dicho fenmeno.
Para repartir el primer naipe se busca en el arreglo la posicin
paquete[rengln][columna] que sea igual a 1. Esto se logra mediante una estructura for anidada
que vara rengln de 0 a 3 y columna de 0 a 12. A qu naipe corresponde dicho espacio del
arreglo? El arreglo palos ha sido precargado con los cuatro palos, por lo que, para obtener el
palo, se imprime la cadena de caracteres palos[rengln] De igual manera, para obtener el valor
del naipe, se imprime la cadena de caracteres cara[columna] Tambin se imprime la cadena de
. La impresin de esta informacin en el orden correcto permite imprimir cada naipe en la
forma Rey de Trboles, As de Diamantes, etctera.
Procedamos mediante la refinacin descendente paso a paso. La parte superior
simplemente es:
Baraja y reparte 52 naipes.

La primera refinacin da:


Inicializa el arreglo de palos (palos)
Inicializa el arreglo de valores (cara)
Inicializa el arreglo de la baraja (paquete)
Baraja los naipes.
Reparte los 52 naipes.

Baraja los naipes se puede expandir como sigue:


Por cada uno de los 52 naipes
Coloca el nmero del naipe en un espacio desocupado de la baraja seleccionada al azar.

APUNTADORES Y CADENA LECCIN 20

20-26

MIGUEL . TOLEDO MARTNEZ

Reparte los 52 naipes se puede expandir como sigue:


Por cada uno de los 52 naipes
Encuentra un nmero de naipe en el arreglo de la baraja e imprime su valor y palo.

La incorporacin de estas expansiones da la segunda refinacin completa:


Inicializa el arreglo de palos.
Inicializa el arreglo de valores.
Inicializa el arreglo de la baraja.
Por cada uno de los 52 naipes
Coloca el nmero del naipe en un espacio desocupado de la baraja seleccionando al azar.
Por cada uno de los 52 naipes
Encuentra un nmero de naipe en el arreglo de la baraja e imprime su valor y palo.

Coloca el nmero del naipe en un espacio desocupado de la baraja seleccionado al azar


se puede expandir como sigue:
Selecciona al azar el espacio de la baraja.
Mientras ya haya sido seleccionado el espacio de la baraja
Selecciona al azar dicho espacio.
Coloca el nmero del naipe en el espacio seleccionado de la baraja.

Encuentra el nmero de naipe en el arreglo de la baraja e imprime su valor y palo se


puede expandir como sigue:
Por cada espacio del arreglo de la baraja
Si el espacio contiene un nmero de naipe
Imprime el valor y el palo del naipe.

La incorporacin de estas expansiones da la tercera refinacin:


Inicializa el arreglo de palos.
Inicializa el arreglo de valores.
Inicializa el arreglo de la baraja.
Por cada uno de los 52 naipes
Selecciona al azar dicho espacio.
Coloca el nmero de naipe en el espacio seleccionado de la baraja.
Por cada uno de los 52 naipes
Por cada espacio del arreglo de la baraja
Si el espacio contiene un nmero de naipe
Imprime el valor y el palo del naipe.

Con esto se termina el proceso de refinacin. Observe que este programa es ms eficiente
si las partes de barajado y reparticin del algoritmo se combinan, de modo que cada naipe se
reparta a medida que se pone en la baraja. Decidimos programar estas operaciones por separado
debido a que los naipes normalmente se reparten despus de haber sido barajados (no a medida
que se barajan)
APUNTADORES Y CADENA LECCIN 20

20-27

MIGUEL . TOLEDO MARTNEZ

Ejemplo 20.13
En el programa BARAJAS.CPP, se muestra el barajado y reparticin de naipes. Observe el formato de
salida que se utiliza en la funcin repartir():
cout

<< setw(5) << setiosflags(ios::right)


<< wCara[columna] << de
<< setw(8) << setiosflags(ios::left)
<< wPalos[renglon]
<< (baraja % 2 == 0 ? \n : \t);

La instruccin de salida anterior provoca que el valor del naipe se enve a la salida justificado a la derecha
en un campo de 5 caracteres, y que el palo se enve a la salida justificado a la izquierda en un campo de 8
caracteres. La salida se imprime en formado de dos columnas. Si el naipe que se enva a la salida est en la
primera columna, se enva una tabulacin a la salida tras l para saltar a la segunda columna: de otra
manera, se enva un salto de lnea.
/* El siguiente programa: BARAJAS.CPP, baraja y reparte naipes. */
#include <iostream.h>
#include <iomanip.h>
#include <stdlib.h>
#include <time.h>

//Para cout y cin


//Para setw()
//Para srand() y rand()
//Para Time()

void barajar(int[][13]);
void repartir(const int [][13], const char *[], const char *[]);
void main(void)
{
const char *palos[4] = {"Corazones", "Diamantes", "Trboles",
"Espadas"};
const char *cara[13] = {"As", "Dos", "Tres", "Cuatro", "Cinco", "Seis",
"Siete", "Ocho", "Nueve", "Diez", "Sota", "Reina",
"Rey"};
int paquete[4][13] = {0};
srand(time(0));
barajar(paquete);
repartir(paquete, cara, palos);
}//Fin de main()
void barajar(int wPaquete[][13])
{
int renglon, columna;
for(int baraja = 1; baraja <= 52; baraja++)
{
do
{
renglon = rand() % 4;
columna = rand() % 13;
} while(wPaquete[renglon][columna] != 0);
wPaquete[renglon][columna] = baraja;
}//Fin del for
}//Fin de barajar()

APUNTADORES Y CADENA LECCIN 20

20-28

MIGUEL . TOLEDO MARTNEZ

void repartir(const int wPaquete[][13], const char *wCara[],


const char *wPalos[])
{
for(int baraja = 1; baraja <= 52; baraja++)
for(int renglon = 0; renglon <= 3; renglon++)
for(int columna = 0; columna <= 12; columna++)
if(wPaquete[renglon][columna] == baraja)
cout
<< setw(5) << setiosflags(ios::right)
<< wCara[columna] << " de "
<< setw(8) << setiosflags(ios::left)
<< wPalos[renglon]
<< (baraja % 2 == 0 ? '\n' : '\t');
}//Fin de repartir()
Hay una debilidad en el algoritmo de reparticin. Una vez encontrado el nmero que se busca, incluso si se
encuentra al primer intento, las estructuras for internas continan buscando dicho nmero en los elementos restantes
de paquete. En los ejercicios se corrige esta deficiencia.

APUNTADORES Y FUNCIONES
Un apuntador a una funcin contiene la direccin que tiene la funcin en la memoria.
Segn hemos visto el nombre de un arreglo es, en realidad, la direccin inicial en memoria del
primer elemento del mismo. Del mismo modo, un nombre de funcin en realidad es la direccin
inicial en memoria del cdigo que lleva a cabo la tarea de la funcin. Los apuntadores a
funciones pueden ser pasados a las funciones, devueltos de ellas, almacenados en arreglos y
asignados a otros apuntadores a funciones.
Ejemplo 20.14
Para ilustrar el uso de los apuntadores a funciones, hemos modificado el programa de ordenamiento de
burbuja de esta leccin al cual llamamos BURBUJA.CPP, dando lugar al siguiente programa,
BURBUJA2.CPP. Este nuevo programa consiste en main() y en las funciones burbuja(), intercambiar(),
ascendente() y descendente() La funcin burbuja() recibe como argumento un apuntador a una funcin
sea ascendente() o descendente()- adems de un arreglo de enteros y su tamao. El programa le pide al
usuario que seleccione si el arreglo debe ordenarse de manera ascendente o descendente. Si el usuario
indica 1, a burbuja() se le pasa un apuntador a la funcin ascendente(), lo que causa que el arreglo se
ordena de menor a mayor. Si el usuario indica 2, a burbuja() se le pasa un apuntador a la funcin
descendente(), lo que causa que el arreglo se ordene de mayor a menor.
/* El siguiente programa: BURBUJA2.CPP, de ordenamiento de usos mltiples que utiliza
apuntadores a funciones.
*/
#include <iostream.h>
#include <iomanip.h>

//Para cout y cin


//Para setw()

void burbuja(int[], const int, int (*)(int, int));


int ascendente(int, int);
int descendente(int, int);
void main(void)
{
const int TAMANO_ARREGLO = 10;
int orden, contador, arreglo[TAMANO_ARREGLO]

APUNTADORES Y CADENA LECCIN 20

= {2, 6, 4, 8, 10, 12,


89, 68, 45, 37};

20-29

MIGUEL . TOLEDO MARTNEZ

cout

<< "Teclee 1 para ordenar en forma ascendente,\n"


<< "Teclee 2 para ordenar en forma descendente: ";
cin >> orden;
cout
<< "\nDatos en el orden original\n";
for(contador = 0; contador < TAMANO_ARREGLO; contador++)
cout << setw(4) << arreglo[contador];
if(orden == 1)
{
burbuja(arreglo, TAMANO_ARREGLO, ascendente);
cout << "\nDatos en orden ascendente\n";
}//Fin del if
else
{
burbuja(arreglo, TAMANO_ARREGLO, descendente);
cout << "\nDatos en orden descendente\n";
}//Fin del else
for(contador = 0; contador < TAMANO_ARREGLO; contador++)
cout << setw(4) << arreglo[contador];
cout << endl;
}//Fin de main()
void burbuja(int trabajar[], const int tamano, int (*comparar)(int, int))
{
void intercambiar(int *, int *);
for(int pasada = 1; pasada < tamano; pasada++)
for(int contador = 0; contador < tamano -1; contador++)
if((*comparar)(trabajar[contador], trabajar[contador + 1]))
intercambiar(&trabajar[contador], &trabajar[contador + 1]);
}//Fin de burbuja
void intercambiar(int *elemento1Ptr, int *elemento2Ptr)
{
int temporal;
temporal
*elemento1Ptr
*elemento2Ptr
}//Fin de intercambiar()
int ascendente(int a, int b)
{
return b < a;
}//Fin de ascendente()
int descendente(int a, int b)
{
return b > a;
}//Fin de descendente()

APUNTADORES Y CADENA LECCIN 20

= *elemento1Ptr;
= *elemento2Ptr;
= temporal;

//Intercambia si b es menor que a

//Intercambia si b es mayor que a

20-30

MIGUEL . TOLEDO MARTNEZ

El siguiente parmetro aparece en el encabezado de la funcin burbuja():


int ( *comparar )( nt, int)
Esto le dice a burbuja() que espere un parmetro que es un apuntador a una funcin que recibe dos
parmetros enteros y devuelve un resultado entero. Se necesitan parntesis alrededor de *comparar debido
a que * tiene menor precedencia que el parntesis que encierra los parmetros de la funcin. Si no se
hubieran incluido los parntesis, la declaracin habra sido
int *comparar( int, int )
que declara una funcin que recibe dos enteros como parmetros y le devuelve un apuntador a un entero.
El parmetro correspondiente del prototipo de funcin de burbuja() es
Int (*) ( int, int)
Observe que slo se han incluido los tipos, pero, con fines de documentacin, el programador puede incluir
nombres que ignorar el compilador.
La funcin que se le pasa a burbuja() es llamada en una instruccin if como sigue
if((*comparar )( trabajar[ contador], trabajar[contador + 1]))
As como se desreferencia un apuntador a una variable para acceder al valor de sta, se desreferencian los
apuntadores a funciones para ejecutar dichas funciones.
La llamada a la funcin podra haberse hecho sin desreferenciar el apuntador, como en
if ( *comparar( trabajar[ contador ], trabajar[ contador + 1]))
que utiliza el apuntador directamente como nombre de funcin. Preferirnos el primer mtodo, donde la
llamada a una funcin es a travs de un apuntador, pues explcitamente ilustra que compare es un apuntador
a una funcin que se desreferencia para llamar a la funcin. El segundo mtodo para llamar a la funcin a
travs de un apuntador da la apariencia de que comparar() de hecho es una funcin. Esto puede confundir a
algn usuario del programa que quisiera ver la definicin de la funcin comparar() y descubriera que no se
defini en ninguna parte del archivo.
Uno de los usos de los apuntadores a funciones es en los sistemas operados por mens. Al usuario se le
solicita desde un men que d una opcin (por ejemplo, de 1 a 5) Cada opcin es atendida por una funcin
distinta. En un arreglo de apuntadores a funciones se almacenan los apuntadores a todas las funciones. Se
toma la seleccin del usuario como ndice del arreglo y se emplea el apuntador del mismo para llamar a la
funcin.
Ejemplo 20.15
El programa siguiente: APUNTADORES3.CPP presenta un ejemplo general de la mecnica de declaracin
y empleo de un arreglo de apuntadores a funciones. Se definen tres funciones (funcion1(), funcion2() y
funcion3()) que toman un argumento entero y no devuelven nada. Los apuntadores a estas tres funciones se
almacenan en el arreglo f, que se declara como sigue:
void( *f[ 3] ) ( int) = { funcion1, funcion2, funcion3};
La declaracin se lee comenzando por el par de parntesis de la izquierda, f es un arreglo de 3 apuntadores a
funciones que toman un int como argumento y devuelven void". El arreglo se inicializa con los nombres de las
funciones (que, nuevamente, son apuntadores) Cuando el usuario introduce un valor entre 0 y 2, se toma como
ndice del arreglo de apuntadores a funciones. La llamada de la funcin se hace como sigue:
APUNTADORES Y CADENA LECCIN 20

20-31

MIGUEL . TOLEDO MARTNEZ

( *f[opcion]) (opcion);
En la llamada, f[opcion] selecciona el apuntador que se encuentra en la localidad opcion del arreglo. El
apuntador se desreferencia para que llame a la funcin, y opcion se pasa como argumento de la funcin.
Cada funcin imprime el valor de su argumento y su nombre, indicando que se llam de manera correcta.
En los ejercicios se desarrollar un sistema operador por mens.
/* El siguiente programa: APUNTADORES3.CPP, muestra un arreglo de apuntadores a funciones. */
#include <iostream.h>

//Para cout y cin

void funcion1(int);
void funcion2(int);
void funcion3(int);
void main(void)
{
void(*f[3])(int) = {funcion1, funcion2, funcion3};
int opcion;
cout << "Introduzca un nmero entre 0 y 2, 3 para terminar: ";
cin >> opcion;
while(opcion >= 0 && opcion < 3)
{
(*f[opcion])(opcion);
cout << "Introduzca un nmero entre 0 y 2, 3 para terminar: ";
cin >> opcion;
}//Fin de while
cout << "Ejecucin del programa finalizada." << endl;
}//Fin de main()
void funcion1(int a)
{
cout

<< "Introdujo " << a


<< " por ello se llam a funcion1()\n\n";
}//Fin de funcion1()

void funcion2(int b)
{
cout

<< "Introdujo " << b


<< " por ello se llam a funcion2()\n\n";
}//Fin de funcion2()

void funcion3(int c)
{
cout

<< "Introdujo " << c


<< " por ello se llam a funcion3()\n\n";
}//Fin de funcion3()

APUNTADORES Y CADENA LECCIN 20

20-32

MIGUEL . TOLEDO MARTNEZ

INTRODUCCIN AL PROCESAMIENTO DE CARACTERES Y CADENAS


En esta seccin se presentarn algunas de las funciones de la biblioteca estndar que
simplifican el procesamiento de cadenas. Las tcnicas que se estudian aqu son adecuadas para el
desarrollo de editores de texto, procesadores de texto, software de formacin de pginas,
sistemas tipogrficos computarizados y otros tipos de software de procesamiento de texto. Aqu
empleamos cadenas basadas en apuntadores.
FUNDAMENTOS DE LOS CARACTERES Y LAS CADENAS

En C++, los caracteres son los bloques de construccin fundamentales de los programas
fuente. Todos los programas se componen de una secuencia de caracteres que, agrupados de
manera significativa, son interpretados por la computadora como una serie de instrucciones que
sirven para llevar a cabo una tarea. Un programa puede contener constantes de caracteres. Una
constante de carcter es un valor entero representado corno un carcter entre apstrofos. El valor
de dicha constante es el valor entero del carcter en el conjunto de caracteres de la mquina. Por
ejemplo, z representa el valor entero de z (122 en el conjunto de caracteres ASCII) y \n
representa el valor entero del salto de lnea (10 en el conjunto de caracteres ASCII)
Una cadena es una serie de caracteres que se trata como unidad. Una cadena puede
incluir letras, dgitos y diversos caracteres especiales, como +, -, *, /, $ y otros. En C++ las
literales de cadena o constantes de cadena se escriben entre comillas, como sigue:
Miguel ngel Toledo Martnez
Ave. I.P.N. 36
Mxico, D. F.
(201) 555-12121

(un nombre)
(una direccin)
(una ciudad y un estado)
(un nmero telefnico)

Una cadena en C++ es un arreglo de caracteres que termina con el carcter nulo ( \0)
Una cadena se accede por medio de un apuntador al primer carcter de dicha cadena. El valor de
una cadena es la direccin (constante) de su primer carcter. Por lo tanto, en C++ es apropiado
decir que una cadena es un apuntador constante; de hecho, se trata de un apuntador al primer
carcter de la cadena. En este sentido, las cadenas son como los arreglos, pues un nombre de
arreglo tambin es un apuntador (constante) a su primer elemento.
En una declaracin, se puede asignar una cadena a un arreglo de caracteres o a una
variable de tipo char *. Las declaraciones
char color[]
char *colorPtr

= azul;
= azul;

inicializan una variable con la cadena azul. La primera declaracin crea un arreglo de 5
elementos llamado color que contiene los caracteres a, z, u, l y \. La segunda crea una
variable de apuntador colorPtr que apunta a la cadena, azul, que est en alguna parte de la
memoria.
La declaracin char color [] = {azul}; tambin podra escribirse como
char color[] = {a, z, u, l, \0' );

APUNTADORES Y CADENA LECCIN 20

20-33

MIGUEL . TOLEDO MARTNEZ

Al declarar un arreglo de caracteres para contener una cadena, se debe asegurar que es
lo bastante grande para almacenar dicha cadena y su carcter nulo de terminacin. La
declaracin anterior determina automticamente el tamao del arreglo basndose en el nmero
de inicializadores indicados en la lista de iniciacin.
Es posible asignar una cadena a un arreglo por medio de extraccin de flujo utilizando
cin. Por ejemplo, la siguiente instruccin sirve para asignarle una cadena al arreglo de caracteres
palabra[20]
cin >> palabra;

La cadena introducida por el usuario se almacena en palabra. La instruccin previa lee


los caracteres hasta que encuentra un espacio, tabulacin, salto de lnea o indicador de fin de
archivo. Observe que la cadena no debe ser de ms de 19 caracteres, a fin de dejar espacio para
el carcter nulo de terminacin. El manipulador de flujo setw() que se estudi en otra leccin
puede servir para asegurar que la cadena introducida a palabra no exceda el tamao del arreglo.
Por ejemplo, la instruccin
cin >> setw( 20 ) >> palabra;

especifica que cin debe leer un mximo de 19 caracteres y ponerlos en el arreglo palabra,
reservando la 20 localidad del arreglo para el carcter nulo de terminacin de la cadena. El
manipulador de flujo setw() slo se aplica al siguiente valor que se introduzca.
En algunos casos es deseable introducir una lnea de texto completa a un arreglo. Con
este fin, C++ ofrece la funcin cin.getline. sta toma tres argumentos -un arreglo de caracteres
en el que se almacenar la lnea de texto, una longitud y un carcter delimitador.
Por ejemplo, el segmento de programa
char sentencia[80];
cin.getline( sentencia, 80, \n);

declara el arreglo sentencia de 80 caracteres, luego lee del teclado una lnea de texto y la carga
en el arreglo. La funcin termina la lectura de los caracteres cuando encuentra el carcter \n,
cuando se introduce un indicador de fin de archivo o cuando el nmero de caracteres ledos hasta
el momento es de uno menos que la longitud indicada en el segundo argumento (el ltimo
carcter del arreglo se reserva para el carcter nulo de terminacin) Si aparece el carcter
delimitador, se lee y se descarta. El tercer argumento de cin.getline tiene \n como valor
predeterminado, por lo que la llamada de funcin anterior podra haberse escrito as:
cin.getline( sentencia, 80 );
FUNCIONES DE MANIPULACIN DE CADENAS DE LA BIBLIOTECA DE MANEJO DE CADENAS

La biblioteca de manejo de cadenas ofrece muchas funciones tiles para la


manipulacin de cadenas de datos, la comparacin de cadenas, la bsqueda de caracteres y
cadenas dentro de cadenas, la divisin de cadenas en tokens (separacin de cadenas en piezas
lgicas) y la determinacin de la longitud de una cadena. Esta seccin presenta algunas

APUNTADORES Y CADENA LECCIN 20

20-34

MIGUEL . TOLEDO MARTNEZ

funciones comunes de manipulacin de cadenas de la biblioteca de manejo de cadenas (que


pertenece a la biblioteca estndar) Las funciones se resumen en la figura 20.9.
Observe que varias funciones de la figura 20.9 contienen parmetros con el tipo de
datos size_t. Este tipo se define en el archivo de encabezado <stddef.h> (que es un archivo de
encabezado de la biblioteca estndar incluido en muchos otros archivos de encabezado de la
biblioteca estndar, incluyendo <string.h>) como un tipo entero sin signo, como unsigned int y
unsigned long.
Prototipo de funcin

Descripcin de la funcin

char *strcpy( char *s1, const char *s2 )


Copia la cadena s2 al arreglo de caracteres s1. Se devuelve el valor de
s1.
char *strncpy( char *s1, const char *s2, size_t n )
Copia cuando mucho n caracteres de la cadena s2 al arreglo de
caracteres s1. Se devuelve el valor de s1.
char *strcat( char *sl, const char *s2 )
Agrega la cadena s2 al final de la cadena s1. El primer carcter de s2
sobrescribe el carcter nulo de terminacin de s1. Se devuelve el valor
de s1.
char *strncat( char *sl, const char *s2, size_t n )
Agrega cuando mucho n caracteres de la cadena s2 a la cadena s1. El
primer carcter de s2 sobrescribe el carcter nulo de terminacin de s1.
Se devuelve el valor de s1.
int strcmp( const char *sl, const char *s2 )
Compara la cadena s1 con la cadena s2. La funcin devuelve un valor de
0, menor que 0 o mayor que 0 si s1 es igual, menor o mayor que s2,
respectivamente.

Prototipo de funcin

Descripcin de la funcin

int strncmp( const char *sl, const char *s2, size_t n )


Compara hasta n caracteres de la cadena s1 con la cadena s2. La funcin
devuelve un valor de 0, menor que 0 o mayor que 0 si s1 es igual,
menor o mayor que s2, respectivamente.
char *strtok( char *sl, const char *s2 )
Una secuencia de llamadas a strtok divide la cadena s1 en "tokens"
(piezas lgicas como palabras de una lnea de texto) separados por los
caracteres contenidos en la cadena s2. La primera llamada contiene s1
como el primer argumento y las siguientes llamadas que dividen la
misma cadena en tokens contienen NULL como el primer argumento.
Cada llamada devuelve un apuntador al token actual. Si no hay ms
tokens al llamar a la funcin, se devuelve NULL.
size_t strlen( const char *s )
Determina la longitud de la cadena s. Devuelve el nmero de caracteres
que precede al carcter nulo de terminacin.
Figura 20.9. Las funciones de manipulacin de cadenas de la biblioteca manejo de cadenas.

La funcin a strcpy() copia el segundo argumento (una cadena) al primer argumento (un
arreglo de caracteres que debe ser lo bastante grande para almacenar la cadena y su carcter nulo
de terminacin, el cual tambin se copia) La funcin strncpy() es equivalente a strcpy(), excepto
que strncpy() especifica el nmero de caracteres a copiar de la cadena al arreglo. Observe que la
APUNTADORES Y CADENA LECCIN 20

20-35

MIGUEL . TOLEDO MARTNEZ

funcin strncpy() no necesariamente copia el carcter nulo de terminacin del segundo


argumento; slo escribe un carcter nulo de terminacin si el nmero de caracteres a copiar es,
cuando menos, uno ms que la longitud de la cadena. Por ejemplo, si el segundo argumento es
test, slo se escribe un carcter nulo de terminacin si el tercer argumento de strncpy() es
cuando menos 5 (los 4 caracteres de test ms el carcter nulo determinacin) Si el tercer
argumento es mayor que 5, se agregan caracteres nulos al arreglo hasta que se ha escrito el
nmero total de caracteres que indica el tercer argumento.
Ejemplo 20.16
El siguiente programa: COPIAR1.CPP, utiliza strcpy() para copiar la cadena completa del arreglo x al
arreglo y, y emplea strncpy() para copiar los primeros 14 caracteres del arreglo x al arreglo z. Un carcter
nulo (\0) se agrega al arreglo z, ya que la llamada a strncpy() no escribe un carcter nulo de terminacin
(el tercer argumento es menor que la longitud de la cadena del segundo argumento)
/* El siguiente programa: COPIAR1.CPP, muestra el uso de strcpy() y strncpy(). */
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strcpy() y strncpy()

void main (void)


{
char x[] = "Feliz Cumpleaos a Ti";
char y[25], z[15];
cout

<< "La cadena de caracteres en el arreglo x es: " << x


<< "\nLa cadena de caracteres en el arreglo y es: " << strcpy(y, x)
<< '\n';

strncpy(z, x, 16); //No copia el caracter nulo


z[16] = '\0';
cout << "La cadena de caracteres en el arreglo z es: " << z << endl;
}//Fin de main()

La funcin a strcat() aade el segundo argumento (una cadena) al primero (un arreglo
de caracteres que contiene una cadena) El primer carcter del segundo argumento reemplaza el
carcter nulo (\ 0) que termina la cadena del primer argumento. El programador debe
asegurarse de que el arreglo en el que se encuentra la primera cadena sea lo bastante grande para
guardar la combinacin de la primera y segunda cadenas, as como el carcter nulo de
terminacin (copiado de la segunda cadena) La funcin a strncat() agrega una cantidad
especfica de caracteres de la segunda cadena a la primera. Se agrega un carcter nulo de
terminacin al resultado.
Ejemplo 20.17
El programa siguiente: COPIAR2.CPP, muestra las funciones strcat() y strncat()
/* El siguiente programa: COPIAR2.CPP, muestra el uso de las funciones strcat()
y strncat().
*/
#include <iostream.h>
#include <string.h>

APUNTADORES Y CADENA LECCIN 20

//Para cout y cin


//Para strcat() y strncat()

20-36

MIGUEL . TOLEDO MARTNEZ

void main(void)
{
char s1[20]
char s2[]
char s3[40]

= "Feliz ";
= "Ao Nuevo ";
= "";

cout << "s1 = " << s1 << "\ns2 = " << s2;
cout << "\nstrcat(s1, s2)
= " << strcat(s1, s2);
cout << "\nstrncat(s3, s1, 6) = " << strncat(s3, s1, 6);
cout << "\nstrcat(s3, s1)
= " << strcat(s3, s1) << endl;
}//Fin de main()
Ejemplo 20.18
El siguiente programa, COMPARAR.CPP, compara tres cadenas por medio de strcmp() y strncmp() La
funcin strcmp() compara el primer argumento con el segundo, carcter por carcter. La funcin devuelve 0
si las cadenas son iguales, un valor negativo si la primera cadena es menor que la segunda y un valor
positivo si la primera cadena es mayor que la segunda. La funcin strncmp() es equivalente a strcmp(),
excepto porque strncmp() compara hasta un nmero especfico de caracteres. La funcin strncmp() no
compara los caracteres que hay despus de un carcter nulo. El programa imprime los valores enteros que
las llamadas de la funcin devuelven.
/* El siguiente programa: COMPARAR.CPP, muestra el uso de las funciones strcmp() y
strncmp().
*/
#include <iostream.h>
#include <iomanip.h>
#include <string.h>

//Para cout y cin


//Para setw()
//Para strcmp() y strncmp()

void main(void)
{
char *s1 = "Feliz Ao Nuevo";
char *s2 = "Feliz Ao Nuevo";
char *s3 = "Feliz Vacacin";
cout

<< "s1 = " << s1 << "\ns2 = " << s2


<< "\ns3 = " << s3 << "\n\nstrcmp(s1, s2) = "
<< setw(2) << strcmp(s1, s2)
<< "\nstrcmp(s1, s3) = " << setw(2)
<< strcmp(s1, s3) << "\nstrcmp(s3, s1) = "
<< setw(2) << strcmp(s3, s1);

cout

<< "\n\nstrncmp(s1, s3, 6) = " << setw(2)


<< strncmp(s1, s3, 6) << "\nstrncmp(s1, s3, 7) = "
<< setw(2) << strncmp(s1, s3, 7)
<< "\nstrncmp(s3, s1, 7) = "
<< setw(2) << strncmp(s3, s1, 7) << endl;

}//Fin de main()
Para entender lo que significa que una cadena sea mayor que o menor que otra cadena, considere el
proceso de alfabetizacin de una serie de apellidos. El lector, sin duda, pondra Gonzlez antes que Prez,
pues en el abecedario la primera letra de Gonzlez est antes que la primera letra de Prez. Pero el
abecedario es algo ms que una lista de 30 letras; es una lista ordenada de caracteres. Cada letra aparece en
una posicin especfica de la lista. La Z es algo ms que una letra del abecedario; Z es, especficamente el
carcter 30 del alfabeto.

APUNTADORES Y CADENA LECCIN 20

20-37

MIGUEL . TOLEDO MARTNEZ

Cmo sabe la computadora que una letra viene antes que otra? Todos los caracteres se representan
dentro de la computadora como cdigos numricos; cuando la computadora compara dos cadenas, de hecho
est comparando los cdigos numricos de los caracteres que hay en dichas cadenas.
Con el fin de estandarizar las representaciones de los caracteres, la mayora de los fabricantes de
computadoras han diseado sus mquinas para que operen con alguno de dos esquemas de codificacin: el
ASCII (Cdigo Estndar Americano para el Intercambio de Informacin) o el EBCDIC (Cdigo de
Intercambio Decimal Codificado en Binario Extendido) Hay otros esquemas de codificacin, pero estos
dos son los ms difundidos.
ASCII y EBCDIC se llaman cdigos de caracteres o conjuntos de caracteres. Las manipulaciones de
cadenas y caracteres de hecho comprenden la manipulacin de los cdigos numricos correspondientes, y
no de los caracteres mismos. Esto explica por qu los caracteres y los enteros pequeos son intercambiables
en C++. Debido a que tiene sentido decir que un cdigo numrico es mayor que, menor que o igual a otro
cdigo numrico, es posible relacionar varios caracteres o cadenas entre ellos haciendo referencia a los
cdigos de caracteres.

La funcin strtok() sirve para dividir una cadena en una serie de tokens. Un token es una
secuencia de caracteres separada por caracteres delimitadores (por lo general espacios o marcas
de puntuacin) Por ejemplo, en una lnea de texto, cada palabra puede considerarse como un
token y los espacios que las separan pueden verse como delimitadores.
Se necesitan varias llamadas a strtok() para dividir en tokens una cadena (suponiendo
que la cadena contenga ms de un token) La primera llamada a strtok() contiene dos argumentos:
una cadena a dividir en tokens y una cadena que contiene los caracteres que separan los tokens
(es decir, delimitadores) En el siguiente programa, TOKENS.CPP, la instruccin
tokenPtr = strtok( cadena, l);

asigna tokenPtr como apuntador al primer token de cadena. El segundo argumento de strtok(),
, indica que los tokens en cadena estn separados por espacios. La funcin strtok() busca el
primer carcter de cadena que no sea un carcter delimitador (espacio) ste es el inicio del
primer token. Luego encuentra el siguiente carcter delimitador de la cadena y lo reemplaza con
un carcter nulo ( \ 0 ) Esto termina el token actual. La funcin a strtok() guarda un apuntador
al carcter que sigue al token y devuelve un apuntador al token actual.
Las siguientes llamadas a strtok() que continan dividiendo en tokens a cadena
contienen NULL como primer argumento. El argumento NULL indica que la llamada a strtok()
deber continuar dividiendo en tokens a partir de la localidad de cadena guardada por la ltima
llamada a strtok() Si no quedan tokens cuando se llame a strtok(), ste devuelve NULL.
Ejemplo 20.19
El programa TOKENS.CPP, utiliza strtok() para dividir en tokens la cadena Esta es una oracin con 7
tokens. Cada token se imprime por separado. Observe que strtok() modifica la cadena de entrada, por lo
tanto, debe hacer una copia de la cadena si desea volver a utilizarla en el programa tras las llamadas a
strtok()
/* El siguiente programa: TOKENS.CPP, muestra el uso de la funcin strtok(). */
#include <iostream.h>
#include <string.h>

APUNTADORES Y CADENA LECCIN 20

//Para cout y cin


//Para strtok()

20-38

MIGUEL . TOLEDO MARTNEZ

void main(void)
{
char cadena[] = "Esta es una oracin con 7 tokens";
char *tokenPtr;
cout

<< "La cadena a dividirse en tokens es:\n" << cadena


<< "\n\nLos tokens son:\n";

tokenPtr = strtok(cadena, " ");


while(tokenPtr != NULL)
{
cout << tokenPtr << '\n';
tokenPtr = strtok(NULL, " ");
}//Fin de while
}//Fin de main()

La funcin strlen() toma como argumento una cadena y devuelve el nmero de caracteres que
sta contiene en la longitud no se incluye el carcter nulo de terminacin.
Ejemplo 20.20
El programa siguiente, LONGITUD.CPP, muestra el uso de la funcin strlen()
/* El siguiente programa: LONGITUD.CPP, muestra el empleo de la funcin strlen(). */
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strlen()

void main(void)
{
char *cadena1 = "abcdefghijklmnopqrstuvwxyz";
char *cadena2 = "cinco";
char *cadena3 = "Mxico";
cout

<< "La longitud de \"" << cadena1


<< "\" es " << strlen(cadena1)
<< "\nLa longitud de \"" << cadena2
<< "\" es " << strlen(cadena2)
<< "\nLa longitud de \"" << cadena3
<< "\" es " << strlen(cadena3) << endl;

}//Fin de main()

A continuacin se resuelven una serie de ejercicios con la intencin de que el lector


estudie por si mismo lo que los programas hacen y como lo hacen. Independiente de lo anterior
en el mismo programa se da una breve definicin del problema.

APUNTADORES Y CADENA LECCIN 20

20-39

MIGUEL . TOLEDO MARTNEZ

EJERCICIOS RESUELTOS

/* El siguiente programa: ALFABETO.CPP, define una cadena de 256 caracteres y


luego se le agrega las letras maysculas del alfabeto.
*/
#include <iostream.h>

//Para cout y cin

void main (void)


{
char cadena[256];
int i;
for (i = 0; i < 26; i++)
cadena[i] = 'A' + i;
cadena[i] = NULL;
cout << "El contenido de la cadena es: " << cadena;
}//Fin de main()

/* El siguiente programa: ALFABETO2.CPP, asigna las letras maysculas de la A a la Z


a una cadena de caracteres. Posteriormente se agrega el caracter NULL al
elemento cadena[10].
*/
#include <iostream.h>

//Para cout y cin

void main (void)


{
char cadena[256];
int i;
for (i = 0; i < 26; i++)
cadena[i] = 'A' + i;
cadena[10] = NULL;
cout << "La cadena contiene: " << cadena;
}//Fin de main()

APUNTADORES Y CADENA LECCIN 20

20-40

MIGUEL . TOLEDO MARTNEZ

/* El siguiente programa: COMILLAS.CPP, usa la secuencia de escape \" para colocar


comillas dentro de la declaracin de una constante.
*/
#include <iostream.h>

//Para cout y cin

void main(void)
{
char cadena[] = "\"!Alto!\", dijo l.";
cout << cadena;
}//Fin de main()

/* El siguiente programa: GETLINE.CPP, utiliza el objeto getline() para leer una


serie de caracteres desde el teclado. Posteriormente visualiza esta cadena
de caracteres, caracter por caracter. Finalmente muestra la longitud de la
cadena.
*/
#include <iostream.h>

//Para cout y cin

void main (void)


{
char cadena[256];
int i;

// Cadena introducida por el usuario


// ndice a utilizar en la cadena

cout << "Digite una cadena de caracteres y oprima enter: ";


cin.getline(cadena, sizeof(cadena), '\n');
// Visualizar cada elemento de la cadena hasta que encuentre el caracter NULL
for (i = 0; cadena[i] != NULL; i++)
cout << cadena[i];
cout << endl << "El nmero de caracteres de la cadena es de: " << i;
}//Fin de main()

/* El siguiente programa: LONGITUD2.CPP, ilustra el uso de la funcin strlen().


Esta funcin regresa la cantidad de caracteres que hay en el parmetro cadena.
El tipo resultante size_t tiene un typedef de tipo unsigned int.
*/
#include <iostream.h>
#include <string.h>

APUNTADORES Y CADENA LECCIN 20

//Para cout y cin


//Para strlen()

20-41

MIGUEL . TOLEDO MARTNEZ

void main(void)
{
char tituloLibro[] = "\"El Coronel no tiene quien le escriba\"";
cout

<< tituloLibro << " contiene " << strlen(tituloLibro)


<< " caracteres";

}//Fin de main()
/* ************************************************************************
Una posible implementacin de la funcin strlen(), es la siguiente:
size_t strlen(const char *cadena)
{
int i = 0;
while (cadena[i])
i++;
return (i)
}//Fin de strlen()
**************************************************************************** */

/* El siguiente programa: COPIAR3.CPP, ilustra el uso de la funcin strcpy().


El prototipo para la funcin strcpy es: char *strcpy(char *destino, const char *fuente)
La funcin copia los caracteres de la cadena fuente a la cadena destino.
La funcin supone que la cadena de destino tiene suficiente espacio para
contener la cadena de origen.
*/
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strcpy()

void main(void)
{
char titulo[] = "\"El Coronel no tiene quien le escriba\"";
char libro[128];
strcpy(libro, titulo);
cout << "El nombre del libro es: " << libro << endl;
}//Fin de main()
/* ***********************************************************************
Una implementacin de la funcin strcpy() podra ser la siguiente:
char *strcpy(char *destino, const char *fuente)
{
while (*destino++ = *fuente++)
;
return (destino - 1);
}//Fin de *strcpy()
**************************************************************************/
/* El siguiente programa: ANEXAR.CPP, ilustra el uso de la funcin strcat().
La funcin aade el contenido de la cadena fuente a la cadena de destino
y regresa el apuntador a la cadena de destino. La funcin asume que la cadena
de destino puede acomodar los caracteres de la cadena de origen.
*/
APUNTADORES Y CADENA LECCIN 20

20-42

MIGUEL . TOLEDO MARTNEZ

#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strcat()

void main(void)
{
char nombre[64] = "Miguel Angel es tan";
strcat(nombre, " feliz");
cout << nombre;
}//Fin de main()
/* ***********************************************************************
Una posible implementacin de la funcin strcat es la siguiente:
char *strcat(char *destino, const char *fuente)
{
char *original = destino;
while (*destino)
destino**;

// Busca el final de la cadena

while (*destino++ = *fuente++)


;
return (original)
}//Fin de *strcat()
************************************************************************** */
/* El siguiente programa: ANEXAR2.CPP, ilustra el uso de la funcin strncat().
La funcin aade num caracteres de la cadena origen a la cadena de destino
y regresa un apuntador a la cadena de destino.
*/
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strncat()

void main(void)
{
char nombre[64] = "Miguel Angel";
strncat(nombre, " Martnez", 4);
cout << "Votaste por?: " << nombre << endl;
}//Fin de main()
/* ***********************************************************************
Una posible implementacin de la funcin strncat() es la siguiente:
char *strncat(char *destino, const char *fuente, int n)
{
char *original = destino;
int i = 0;
while (*destino)
destino++;
while ((i++ < n) && (*destino++ = *fuente++))
;

APUNTADORES Y CADENA LECCIN 20

20-43

MIGUEL . TOLEDO MARTNEZ

if (i > n)
*destino = NULL;
return (original);
}//Fin de strncat()
************************************************************************** */
/* El siguiente programa: STRXFRM.CPP, ilustra el uso de la funcin strxfrm() */
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strxfrm()

void main(void)
{
char fuente[64] = "\El Coronel no tiene quien le escriba\"";
char destino[64];
int longitud;
longitud = strxfrm(destino, fuente, sizeof(fuente));
cout << "Longitud de la cadena: " << longitud << endl;
cout << "Contenido del destino: " << destino << endl;
cout << "Contenido de la fuente: " << fuente << endl;
}//Fin de main()

/* El siguiente programa: STREQL.CPP, ilustra el uso de la funcin streql() */


#include <iostream.h>

//Para cout y cin

int streql(char *cadena1, char *cadena2)


{
while ((*cadena1 == *cadena2) && (*cadena1))
{
cadena1++;
cadena2++;
}//Fin de while
return((*cadena1 == NULL) && (*cadena2 == NULL));
}//Fin de streql()
void main(void)
{
cout << "Comparando Abc y Abc : " << streql("Abc", "Abc") << endl;
cout << "Comparando abc y Abc : " << streql("abc", "Abc") << endl;
cout << "Comparando abcd y abc: " << streql("abcd", "abc") << endl;
}//Fin de main()

APUNTADORES Y CADENA LECCIN 20

20-44

MIGUEL . TOLEDO MARTNEZ

/* El siguiente programa: STRIEQL.CPP, ilustra el uso de la funcin strieql() */


#include <iostream.h>
#include <ctype.h>

//Para cout y cin


//Para toupper()

int strieql(char *cadena1, char *cadena2)


{
while ((toupper(*cadena1) == toupper(*cadena2)) && (*cadena1))
{
cadena1++;
cadena2++;
}//Fin de strieql()
return((*cadena1 == NULL) && (*cadena2 == NULL));
}//Fin de strieql()
void main(void)
{
cout << "Comparando Abc y Abc : " << strieql("Abc", "Abc") << endl;
cout << "Comparando abc y Abc : " << strieql("abc", "Abc") << endl;
cout << "Comparando abcd y abc: " << strieql("abcd", "abc") << endl;
}//Fin de main()

/* El siguiente programa: STRLWR.CPP, ilustra el uso de la funciones strlwr() y


strupr().
*/
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strlwr() y strupr()

void main(void)
{
cout << strlwr("\"El Coronel no tiene quien le escriba\"") << endl;
cout << strupr("\"El Coronel no tiene quien le escriba\"") << endl;
}//Fin de main()
/* *********************************************************************
Una posible implementacin de la funcin strlwr() es la siguiente:
#include <ctype.h>
char *strlwr(char *cadena)
{
char *original = cadena;
while (*cadena)
{
*cadena = tolower(*cadena);
cadena++;
}//Fin de while
return(original)
}//Fin de *strlwr()
************************************************************************** */

APUNTADORES Y CADENA LECCIN 20

20-45

MIGUEL . TOLEDO MARTNEZ

/* El siguiente programa: STRCHR.CPP, ilustra el uso de la funcin strchr */


#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strchar()

void main(void)
{
char titulo[64] = "\"El Coronel no tiene quien le escriba\"";
char *ptr;
if (ptr = strchr(titulo, 'C'))
cout
<< "La primera ocurrencia de C es en el desplazamiento: "
<< (ptr - titulo) << endl;
else
cout << "NO SE ENCONTR EL CARCTER BUSCADO!" << endl;
}//Fin de main()
/* ***********************************************************************
Una posible implementacin de la funcin strchr() es la siguiente:
char *strchr(const char *cadena, int letra)
{
while ((*cadena != letra) && (*cadena)
cadena++;
return(cadena);
}//Fin de *strchr()
************************************************************************** */
/* El siguiente programa: STRRCHR.CPP, ilustra el uso de la funcin strrchr */
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strrchr()

char *strrchr(char *cadena, char letra)


{
char *ptr = NULL;
while (*cadena)
{
if (*cadena == letra)
ptr = cadena;
cadena++;
}//Fin de while
return(ptr);
}//Fin de *strrchr()
void main(void)
{
char titulo[64] = "\"El Coronel no tiene quien le escriba\"";
char *ptr;
if (ptr = strrchr(titulo, 'n'))
cout
<< "La ltima vez en que se encontr la letra n a la derecha: "
<< (ptr - titulo) << endl;
else
cout
<< "!NO SE ENCONTR EL CARCTER BUSCADO!" << endl;
}//Fin de main()

APUNTADORES Y CADENA LECCIN 20

20-46

MIGUEL . TOLEDO MARTNEZ

/* ***********************************************************************
Una posible implementacin de la funcin strrchr() es la siguiente:
char *strrchr(const char *cadena, int letra)
{
char *ptr = NULL;
while (*cadena)
{
if (*cadena == letra)
ptr = cadena;
cadena++;
}//Fin de while
return(ptr);
}//Fin de *strrch()
************************************************************************** */

FUNCIONES DE CADENA QUE UTILIZAN CADENAS UBICADAS FUERA DE LOS 64KB (far string)

Algunos compiladores poseen funciones que aceptan apuntadores ubicados fuera de los
64 kbyes. Por ejemplo, para determinar la longitud de una cadena referenciada por un apuntador
far, puede utilizar la funcin _fstrlen(), como se muestra a continuacin:
#include <string.h>
size_t _fstrlen(const char *string)

En el caso de que no exista en el compilador y usted tenga una funcin que maneje
apuntadores ubicados en los 64 kbytes, puede modificar los mismos como se muestra en el
siguiente ejemplo, para la funcin fstreql():
int fstreql (char far *str1, char far *str2)
{
while ((*str1 == *str2) && (*str1))
{
str1++;
str2++;
}//Fin de while
return ((*str1 == NULL) && (*str2 ==NULL));
}//Fin de fstreql()

NUMERO DE OCURRENCIAS DE UN CARCTER DENTRO DE UNA CADENA.

A continuacin escribiremos la funcin chrcnt() que resuelve el problema planteado:


int chrcnt (const char *cadena, int letra)
{
int contador = 0;
while (*cadena)
if (*cadena == letra)
contador++;
return
(contador);
APUNTADORES Y CADENA
LECCIN
20
}//Fin de chrcnt()

20-47

MIGUEL . TOLEDO MARTNEZ

A continuacin escribiremos la funcin strrev() que invierta una cadena.


char *strrev (char *cadena)
{
char *original = cadena;
char *adelante = cadena;
char temp;
while (*cadena)
cadena++;
while (adelante < cadena)
{
temp = *(--cadena);
*cadena = *adelante;
*adelante++ = temp;
}//Fin de while()
return (original);
}//Fin de *strrev()

A continuacin escribiremos la funcin strset() sustituye todos los caracteres de una


cadena por un carcter especificado.
char * strset (char *cadena, int letra)
{
char *original = cadena;
while (*cadena)
*cadena++ = letra;
return (original);
}//Fin de *strset()

/* El siguiente programa: STRCMP.CPP, ilustra el uso de la funcin strcmp(), la


cual compara dos cadenas por menor, mayor o igual. Devolviendo 0 si ambas son
iguales, si la primera es mayor que la segunda devuelve un nmero negativo,
finalmente si la segunda es mayor que la primera devuelve un nmero positivo.
*/
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strcmp()

void main(void)
{
cout << "Comparando Abc y Abc : " << strcmp("Abc", "Abc") << endl;
cout << "Comparando abc y Abc : " << strcmp("abc", "Abc") << endl;
cout << "Comparando abcd y abc: " << strcmp("abcd", "abc") << endl;
cout << "Comparando Abc y Abcd: " << strcmp("Abc", "Abcd") << endl;
}//Fin de main()

APUNTADORES Y CADENA LECCIN 20

20-48

MIGUEL . TOLEDO MARTNEZ

/* ***********************************************************************
Una posible implementacin de la funcin strcmp() es la siguiente:
int strcmp(const char *s1, const char *s2)
{
while ((*s1 == *s2) && (*s1))
{
s1++;
s2++;
}//Fin de while
if ((*s1 == *s2) && (! *s1) // Las cadenas son iguales
return(o);
else if ((*s1) && (! *s2)) // cadena s1 > cadena s2
return(-1);
else if ((*s2) && (! *s1)) // cadena s2 > cadena s1
return(1);
else
return ((*s1 > *s2) ? -1; 1);
}//Fin de strcmp()
************************************************************************** */

/* El siguiente programa: STRNCMP.CPP, muestra la funcin strncpm(), la cual


compara cierto numero de caracteres de una cadena con otra.
*/
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strncmp()

void main(void)
{
cout << "Comparando tres letras de Abc con Abc: " <<
strncmp("Abc", "Abc", 3) << endl;
cout << "Comparando tres letras de abc con Abc: " <<
strncmp("abc", "Abc", 3) << endl;
cout << "Comparando tres letras de abcd con abc: " <<
strncmp("abcd", "abc", 3) << endl;
cout << "Comparando cinco letras de Abc con Abcd: " <<
strncmp("Abc", "Abcd", 5) << endl;
}//Fin de main()

APUNTADORES Y CADENA LECCIN 20

20-49

MIGUEL . TOLEDO MARTNEZ

/* ***********************************************************************
Una posible implementacin de la funcin strncmp() es la siguiente:
int strncmp(const char *s1, const char *s2, int n)
{
int i = o;
while ((*s1 == *s2) && (*s1) && i < n)
{
s1++;
s2++;
i++
}//Fin de while
if (i == n)
// Las cadenas son iguales
return (0);
else if ((*s1 == *s2) && (! *s1) // Las cadenas son iguales
return(o);
else if ((*s1) && (! *s2))
// cadena s1 > cadena s2
return(-1);
else if ((*s2) && (! *s1))
// cadena s2 > cadena s1
return(1);
else
return ((*s1 > *s2) ? -1; 1);
}//Fin de strncmp()
************************************************************************** */
/* El siguiente programa: CMPCASE.CPP, ilustra el uso de las funciones stricmp()
y strncmpi(), las cuales hacen comparaciones sin diferenciar maysculas de
minsculas.
*/
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para stricmp() y strncmpi()

void main(void)
{
cout
cout
cout
cout

<< "Comparando Abc con Abc: " << stricmp("Abc", "Abc") << endl;
<< "Comparando abc con Abc: " << stricmp("abc", "Abc") << endl;
<< "Comparando tres letras de abcd con ABC: "
<< strncmpi("abcd", "ABC", 3) << endl;
<< "Comparando 5 letras de abc con Abcd: "
<<
strncmpi("abc", "Abcd", 5);

}//Fin de main()

APUNTADORES Y CADENA LECCIN 20

20-50

MIGUEL . TOLEDO MARTNEZ

/* La mayora de los compiladores ofrecen un conjunto de funciones para convertir


caracteres ASCII a nmeros, vase la tabla siguiente:
funcin
atof()
atoi()
atol()
strtod()
strtol()

propsito
Convierte de ascii a punto flotante.
Convierte de ascii a entero.
Convierte de ascii a entero largo.
Convierte de ascii a doble precisin.
Convierte de ascii a entero largo.

El siguiente programa: ASCIINUM.CPP, ilustra el uso de algunas de las funciones anteriores.


*/
#include <iostream.h>
#include <stdlib.h>
#include <iomanip.h>

//Para cout y cin


//Para las funciones anteriores
//Para setprecision()

void main(void)
{
int intResultado;
float floatResultado;
long longResultado;
intResultado = atoi("1234");
floatResultado = atof("12345.678");
longResultado = atol("1234567L");
cout

<< intResultado << ' ' << setprecision(8) << floatResultado


<< ' ' << longResultado;

}//Fin de main()

/* El siguiente programa: STRDUP.CPP, ilustra el uso de la funcin strdup() para


copiar una cadena de caracteres a un rea dinmica de la memoria.
*/
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strdup()

void main(void)
{
char *titulo;
if ((titulo = strdup("\"El Coronel no tiene quien le escriba\"")))
cout << "Ttulo: " << titulo << endl;
else
cout << "Error al duplicar la cadena" << endl;
}//Fin de main()

APUNTADORES Y CADENA LECCIN 20

20-51

MIGUEL . TOLEDO MARTNEZ

/* ***********************************************************************
Una posible implementacin de la funcin strdup() es la siguiente:
#include <string.h>
#include <malloc.h>

//Para cout y cin


//Para malloc()

char *strdup(const char *s1)


{
char *ptr;
if ((ptr = malloc(strlen(s1))))
strcpi(ptr,s1);

// Localiza area dinmica de memoria

return(ptr);
}//Fin de *strdup()
************************************************************************** */

/* El siguiente programa: STRSPN.CPP, ilustra el uso de la funcin strspn().


Esta funcin busca en una cadena por la primera ocurrencia de cualquiera
de los caracteres de una subcadena. La funcin devuelve el desplazamiento
dentro de la cadena del primer caracter que no esta contenido en la segunda
cadena especificada.
*/
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strspn()

void main(void)
{
cout

<< "Buscando la subcadena Abc en la cadena AbcDef: "


<< strspn("AbcDef", "Abc") << endl;

cout

<< "Buscando la subcadena cbA en la cadena AbcDef: "


<< strspn("AbcDef", "cbA") << endl;

cout

<< "Buscando la subcadena Def en AbcAbc: "


<< strspn("AbcAbc", "Def");

}//Fin de main()
/* **********************************************************************
Una posible implementacin de la funcin strspn es la siguiente:
size_t strspn(const char *s1, const char *s2)
{
int i, j;
for (i = 0; *s1; i++, s1++)
{
for (j = 0; s2[j]; j++)
if (*s1 == s2[j])
break;
if (s2[j] == NULL);
break;
}//Fin del for
return (i);
}//Fin de sstrspn()
************************************************************************** */
APUNTADORES Y CADENA LECCIN 20

20-52

MIGUEL . TOLEDO MARTNEZ

/* El siguiente programa: STRSTR.CPP, ilustra el uso de la funcin strstr(), la


cual busca una subcadena dentro de una cadena. Si la encuentra devuelve un
apuntador a la primera ocurrencia de la subcadena. Si no la encuentra devuelve
NULL.
*/
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strstr()

void main(void)
{
cout

<< "Buscando la subcadena Abc en la cadena AbcDef: "


<< (strstr("AbcDef", "Abc") ? "Encontrado" : "No encontrado")
<< endl;

cout

<< "Buscando la subcadena Abc en la cadena abcDef: "


<< (strstr("abcDef", "Abc") ? "Encontrado" : "No encontrado")
<< endl;

cout

<< "Buscando la subcadena Abc en la cadena AbcAbc: "


<< (strstr("AbcAbc", "Abc") ? "Encontrado" : "No encontrado");

}//Fin de main()
/* ***********************************************************************
Una posible implementacin de la funcin strstr() es la siguiente:
char *strstr(const char *s1, const char *s2)
{
int i, j, k;
for (i = 0; s1[i]; i++)
for (j = i, k = 0; s1[j] == s2[k]; j++, k++)
if (! s2[k + 1])
return (s1 + i);
return (NULL);
}//Fin de *strstr()
************************************************************************** */

CONTAR EL NMERO DE OCURRENCIA DE UNA SUBCADENA DENTRO DE UNA CADENA

La siguiente funcin: strstr_cnt() cuenta el nmero de veces en el que una subcadena se


encuentra dentro de una cadena.
int strstr_cnt (const char *cadena, const char *subcadena)
{
int i, j, k, contador = 0;
for (i = 0; cadena[i]; i++)
for (j = i, k = 0; cadena[j] == subcadena[k]; j++, k++)
if (! subcadena[k + 1])
contador++;
return (contador);
}//Fin de strstr_cnt()

APUNTADORES Y CADENA LECCIN 20

20-53

MIGUEL . TOLEDO MARTNEZ

OBTENER UN NDICE A UNA SUBCADENA

La siguiente funcin strstr_index(), devuelve el ndice donde se encuentra la subcadena


dentro de la cadena:
char *strstr_index(const char *s1, const char *s2)
{
int i, j, k;
for (i = 0; s1[i]; i++)
for (j = i, k = 0; s1[j] == s2[k]; j++, k++)
if (! s2[k + 1])
return (i);
return (i);
}//Fin de *strstr_index()

OBTENER LA OCURRENCIA MAS A LA DERECHA DE UNA SUBCADENA

La funcin r_strstr() devuelve un apuntador a la ocurrencia de ms a la derecha de una


subcadena dentro de una cadena:
char *r_strstr(const char *s1, const char *s2)
{
int i, j, k , izquierda = 0;
for (i = 0; s1[i]; i++)
for (j = i, k = 0; s1[j] == s2[k]; j++, k++)
if (! s2[k + 1])
izquierda = i;
return ((izquierda) ? s1 + izquierda: NULL);
}//Fin de *r_strstr()

REMOVER UNA SUBCADENA CONTENIDA DENTRO DE UNA CADENA

La siguiente funcin strstr_rem() remueve la primera ocurrencia de una subcadena


contenida en una cadena.
char *strstr_rem (char *cadena, char *subCadena)
{
int i, j, k, loc = -1;
for (i = 0; cadena[i] && (loc == -1); i++)
for (j =i, k = 0; str[j] == subCadena[k]; j++, k++)
if (! subCadena[k + 1] )
loc = i;
if (loc != -1)
{

// La cadena fue encontrada


for (k = 0; subCadena[k]; k++)
for (j = loc, i = loc + k; cadena[i]; j++, i++)
cadena[j] = cadena[i];

APUNTADORES Y CADENA LECCIN 20

20-54

MIGUEL . TOLEDO MARTNEZ

cadena [i] == NULL;


}//Fin de if
return (cadena);
}//Fin de *strstr_rem

REEMPLAZO DE UNA SUBCADENA POR OTRA

La siguiente funcin: strstr_rep(), reemplaza la primera ocurrencia de una subcadena por


otra subcadena.
#include <string.h>
char *strsstr_rep (char *fuente, char *viejo, char *nuevo)
{
char *original = fuente;
char temp[256];
int longitudVieja = strlen(viejo);
int i, j, k, localizacion = -1;
for (i = 0; fuente[i] && (localizacion == -1); ++i)
for (j = i, k = 0; fuente[j] == viejo[k]; j++, k++)
if (! viejo[k + 1])
localizacion = i;
if (localizacion != -1)
{
for (j = 0; j < localizacion; j++)
temp[j] = fuente[j];
for (i = 0; nuevo[i]; i++, j++)
temp[j] = nuevo[i];
for (k = localizacion + longitudVieja; fuente[k]; k++, j++)
temp[j] = fuente[k];
temp[j] = NULL;
for (i = 0; fuente[i] = temp[i]; i++)//Ciclo nulo
;
}//Fin de if
return (original);
} //Fin de *strstr_rep()

APUNTADORES Y CADENA LECCIN 20

20-55

MIGUEL . TOLEDO MARTNEZ

DETERMINAR SI UN CARCTER ES ALFANUMRICO

Un carcter es alfanumrico si es letra o dgito. El archivo de cabecera ctype.h contiene


una macro llamada isalnum que determina si un carcter es alfanumrico:
if (isalnum(letra))

Considere la siguiente implementacin de dicha macro:


#define isalnum(c) ((toupper( (c) ) >= A ) && (toupper ((c)) <= Z) ||
((c) >= 0 ) && ((c) <= 9))

DETERMINAR SI UN CARCTER ES UNA LETRA DEL ALFABETO

El archivo de cabecera ctype.h proporciona la macro isalpha para determinar si un


carcter es una letra:
if (isalpha(carcter))

Considere la siguiente implementacin de dicha macro:


#define isalpha(c) (toupper((c)) >= A && (toupper((c)) <= Z)

DETERMINAR SI UN CARCTER CONTIENE UN VALOR ASCII

Un valor es ASCII si se encuentra en el rango comprendido entre 0 y 127. El archivo


cabecera ctype.h contiene la macro isascii que le ayuda a determinar si un carcter contiene un
valor ASCII:
if (isascii(carcter))

Considere la siguiente implementacin de dicha macro:


#define isascii(ltr) ((unsigned) (ltr) < 128)

DETERMINAR SI UN CARCTER ES UN CARCTER DE CONTROL

Un carcter de control es un valor comprendido entre ^A y ^Z o ^a y ^z. El archivo


cabecera ctype.h contiene la macro iscntrl que le ayuda a determinar si un carcter es de control:
if (iscntrl(carcter))

DETERMINAR SI UN CARCTER ES UN DIGITO

Un dgito es un valor ASCII comprendido entre 0 y 9. El archivo de cabecera ctype.h


contiene la macro isdigit que le ayuda a determinar si un carcter es o no dgito:
if (isdigit(carcter))

Considere la siguiente implementacin de dicha macro:


#define isdigit(c) ((c) >= 0 && (c) <= 9)
APUNTADORES Y CADENA LECCIN 20

20-56

MIGUEL . TOLEDO MARTNEZ

DETERMINAR SI UN CARCTER ES UN CARCTER GRAFICO

Un carcter es grfico si es un carcter imprimible (ver isprint), excluyendo el carcter


espacio (ASCII 32) El archivo de cabecera ctype.h contiene la macro isgraph que le ayuda a
determinar si un carcter es o no grfico:
if (isgraph(carcter))

Considere la siguiente implementacin de dicha macro:


#define isgraph(ltr) ((ltr) >=33) && ((ltr) <= 127)

DETERMINAR SI UN CARCTER ES MAYSCULA O MINSCULA

El archivo de cabecera ctype.h contiene las macros islower y isupper que le ayudan a
determinar si un carcter es minscula o mayscula respectivamente:
if (islower(carcter))
if(isupper(carcter))

Considere las siguiente implementaciones de dichas macros:


#define islower(c) ((c) >= a && (c) <= z)
#define isupper(c) ((c) >= A && (c) <= Z)

DETERMINAR SI UN CARCTER ES IMPRIMIBLE

Un carcter es imprimible si se encuentra en el rango comprendido entre 32 y 127. El


archivo de cabecera ctype.h contiene la macro isprint que le ayuda a determinar si un carcter es
o no imprimible:
if (isprint(carcter))

Considere la siguiente implementacin de dicha macro:


#define isprint(ltr) ((ltr) >=32) && ((ltr) <= 127)

DETERMINAR SI UN CARCTER ES UN SMBOLO DE PUNTUACIN

Desde el punto de vista gramatical los smbolos de puntuacin incluyen a la coma, punto
y coma, punto, interrogacin y as sucesivamente. Desde punto de vista del lenguaje C un
smbolo es de puntuacin si es un carcter grfico que no sea alfanumrico. El archivo de
cabecera ctype.h contiene la macro ispunct que le ayuda a determinar si un carcter es o no
smbolo de puntuacin:
if (ispunct(carcter))

Considere la siguiente implementacin de dicha macro:


#define ispunct(c) (isgraph(c)) && ! isalphanum((c)))

APUNTADORES Y CADENA LECCIN 20

20-57

MIGUEL . TOLEDO MARTNEZ

DETERMINAR SI UN CARCTER ES EL CARCTER ESPACIO

El carcter espacio incluye el espacio, tabulador, retorno de carro, nueva lnea, tabulador
vertical y alimentacin de hoja. El archivo de cabecera ctype.h contiene la macro isspace que le
ayuda a determinar si un carcter es o no carcter espacio:
if (isspace(carcter))

Considere la siguiente implementacin de dicha macro:


#define isspace(c) (((c) == 32) || ((c) == 9) || ((c) == 13))

DETERMINAR SI UN CARCTER ES UN VALOR HEXADECIMAL

Un carcter es hexadecimal si es un dgito entre 0 y 9 o una letra entre A y F. El archivo


de cabecera ctype.h contiene la macro isxdigit que le ayuda a determinar si un carcter es o no un
carcter hexadecimal:
if (isxdigit(carcter))

Considere la siguiente implementacin de dicha macro:


#define isxdigit(c) (isnum((c)) || (toupper((c)) >= A && (toupper((c)) <= F))
/* El siguiente programa: TOUPPER.CPP, ilustra el uso de la macro _toupper y
la funcin toupper. Se observa el error que puede ocurrir cuando se utilizan
letras maysculas con la macro _toupper.
*/
#include <stdio.h>
#include <ctype.h>

//Para putchar
//Para _toupper() y toupper()

void main (void)


{
char cadena[] = "\"El Coronel No Tiene Quien Le Escriba\"";
int i;
for (i = 0; cadena[i]; i++)
putchar(toupper(cadena[i]));
putchar('\n');
for (i = 0; cadena[i]; i++)
putchar(_toupper(cadena[i]));
putchar('\n');
}//Fin de main()

APUNTADORES Y CADENA LECCIN 20

20-58

MIGUEL . TOLEDO MARTNEZ

/* El siguiente programa: TOLOWER.CPP, ilustra el uso de la macro _tolower y


la funcin tolower. Se observa el error que puede ocurrir cuando se utilizan
letras minscula con la macro _tolower.
*/
#include <stdio.h>
#include <ctype.h>

//Para putchar()
//Para tolower() y _tolower()

void main (void)


{
char cadena[] = "\"El Coronel No Tiene Quien Le Escriba\"";
int i;
for (i = 0; cadena[i]; i++)
putchar(tolower(cadena[i]));
putchar('\n');
for (i = 0; cadena[i]; i++)
putchar(_tolower(cadena[i]));
putchar('\n');
}//Fin de main()

CARCTER ASCII VLIDO

Para asegurar que un carcter es un carcter ASCII vlido, su valor se encuentra entre 0 y
127 utilice la macro toascii contenida en archivo de cabecera ctype.h:
Una implementacin de dicha macro es la siguiente:
#define toascii(character) ((character) & 0x7F)

Lo que se logra con el valor hexadecimal 0x7F es hacer que el valor ASCII sea siempre
positivo (0 a 127)
/* La funcin printf, le permite escribir salida formateada a la pantalla.
Dependiendo de los requerimientos de su programa, existen ocasiones en que
necesita trabajar con una cadena de caracteres que contiene salida formatea
da. Por ejemplo, suponga que sus empleados tienen 5 dgitos como nmero de
empleado y tres caracteres que identifican la regin (tal como OAX para Oaxaca).
Suponga que almacena informacin acerca de cada empleado en un archivo cuyo
nombre es una combinacin de estos dos valores (tal como OAX12345). La
funcin sprintf le permite escribir salida formateada en una cadena de
caracteres.
El siguiente programa: SPRINTF.CPP, utiliza la funcin sprintf para crear
un nombre de archivo de 8 caracteres.
*/
#include <stdio.h>

//Para la funcin sprintf() y printf()

APUNTADORES Y CADENA LECCIN 20

20-59

MIGUEL . TOLEDO MARTNEZ

void main(void)
{
int numeroEmpleado
= 12345;
char region[]
= "OAX";
char nombreArchivo[64];
sprintf(nombreArchivo, "%s%d", region, numeroEmpleado);
printf("Nombre del empleado: %s\n", nombreArchivo);
}//Fin de main()

/* La funcin scanf() le permite leer entrada formateada desde stdin. Dependiendo


de su programa, existen ocasiones en las cuales una cadena de caracteres
contiene campos que desea asignar a variables especficas. La funcin sscanf()
le permite leer valores de una cadena, asignando los valores a variables
especficas.
El siguiente programa: SSCANF.CPP, ilustra el uso de la funcin sscanf().
*/
#include <stdio.h>

//Para sscanf() y printf()

void main(void)
{
int edad;
float salario;
char cadena[] = "33 25000.00";
sscanf(cadena, "%d %f\n", &edad, &salario);
printf("Edad: %d salario %f\n", edad, salario);
}//Fin de main()
/* El siguiente programa: CADENA1.CPP, utiliza las funciones strlen(), strcat()
busca un caracter en una cadena y lo remplaza por otro caracter.
*/
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strlen(), strcat()

const unsigned MAX1 = 40;


const unsigned MAX2 = 80;
void main(void)
{
char cadenaPequena[MAX1 + 1];
char cadenaGrande[MAX2 + 1];
char caracterBuscar, caracterRemplazar;
cout
<< "Introduzca la cadena pequea: ";
cin.getline(cadenaPequena, MAX1);
cout
<< "Introduzca la cadena grande : ";
cin.getline(cadenaGrande, MAX2);
cout
<< endl << endl;

APUNTADORES Y CADENA LECCIN 20

20-60

MIGUEL . TOLEDO MARTNEZ

cout
cout

<< "La cadena pequena tiene " << strlen(cadenaPequena)


<< " caracteres" << endl;
<< "La cadena grande tiene " << strlen(cadenaGrande)
<< " caracteres" << endl << endl;

// Encadena cadenaPequena a cadenaGrande


strcat(cadenaGrande, cadenaPequena);
cout
cout

<< "Las cadenas encadenadas son: " << cadenaGrande << endl;
<< "La nueva cadena tiene " << strlen(cadenaGrande)
<< " caracteres" << endl << endl;

// Obtiene los caracteres de bsqueda y reemplazo


cout << "Introduzca el caracter a buscar: ";
cin >> caracterBuscar;
cout << "Introduzca el caracter que reemplaza: ";
cin >> caracterRemplazar;
// Reemplaza caracter en la cadena cadenaGrande
for (int i = 0; i < strlen(cadenaGrande); i++)
if (cadenaGrande[i] == caracterBuscar)
cadenaGrande[i] = caracterRemplazar;
// Despliega la cadena cadenaGrande actualizada
cout
<< "\nLa nueva cadena es: " << cadenaGrande;
}//Fin de main()

/* El siguiente programa: CADENA2.CPP, utiliza el mtodo de ordenamiento de la burbuja


para ordenar un arreglo de cadenas de caracteres.
*/
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strcmp(), strcpy()

const unsigned TAMANO_CADENA = 40;


const unsigned TAMANO_ARREGLO = 11;
void main(void)
{
char cadena[TAMANO_ARREGLO][TAMANO_CADENA] = { "California", "Virginia", "Alaska",
"New York", "Michigan", "Nevada",
Ohio", "Florida", "Washington",
"Oregon", "Arizona" };
cout << "El arreglo desordenado es:" << endl;
for (int i = 0; i < TAMANO_ARREGLO; i++)
cout << cadena[i] << endl;
cout << endl;
// Usa el mtodo de ordenamiento de la burbuja para ordenar el arreglo
for (int i = 0; i < TAMANO_ARREGLO; ++i)
for (int j = 0; j < i; ++j)
if (strcmp(cadena[i], cadena[j]) < 0)

APUNTADORES Y CADENA LECCIN 20

20-61

MIGUEL . TOLEDO MARTNEZ

{
char temp[TAMANO_CADENA];
strcpy(temp, cadena[i]);
strcpy(cadena[i], cadena[j]);
strcpy(cadena[j], temp);
}//Fin de if
cout << "El arreglo ordenado es:" << endl;
for (int i = 0; i < TAMANO_ARREGLO; i++)
cout << cadena[i] << endl;
}//Fin de main()

/* El siguiente programa: CADENA3.CPP, convierte una cadena de caracteres en minsculas,


maysculas e invierte la cadena. Si se introduce puras minsculas o puras maysculas,
el programa lo advierte. Tambin advierte si las palabras son palndromos.
*/
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strcpy(), strlwr(), strupr()

const unsigned TAMANO_CADENA = 40;


void main(void)
{
char str1[TAMANO_CADENA+1];
char str2[TAMANO_CADENA+1];
bool esMinuscula, esMayuscula, esSimetrico;
cout << "Introduzca una cadena: ";
cin.getline(str1, TAMANO_CADENA);
cout << "\nLa cadena que introdujo fue: " << str1 << endl;
strcpy(str2, str1);
// Copia str1 a str2
strlwr(str2);
// Convierte a minsculas
esMinuscula = (strcmp(str1, str2) == 0) ? true : false;
cout << "\nMinsculas: " << str2 << endl;
strupr(str2);
// Convierte a maysculas
esMayuscula = (strcmp(str1, str2) == 0) ? true : false;
cout << "Maysculas: " << str2 << endl << endl;
strcpy(str2, str1);
// Copia str1 a str2
strrev(str2);
// Invierte los caracteres
esSimetrico = (strcmp(str1, str2) == 0) ? true : false;
cout << "Invertido: " << str2 << endl;
if (esMinuscula)
cout << "Su entrada no tiene letras maysculas" << endl;
if (esMayuscula)
cout << "Su entrada no tiene letras minsculas" << endl;
if (esSimetrico)
cout << "Su entrada tiene caracteres simtricos" << endl;
}//Fin de main()

APUNTADORES Y CADENA LECCIN 20

20-62

MIGUEL . TOLEDO MARTNEZ

/* El siguiente programa: CADENA4.CPP, busca una subcadena dentro de una cadena e indica
en donde comienza. Busca tambin un caracter indicando en que posicin se encuentra.
*/
#include <iostream.h>
#include <string.h>

//Para cout y cin


//Para strstr(), strchr

const unsigned TAMANO_CADENA = 40;


void main(void)
{
char cadenaPrincipal[TAMANO_CADENA+1];
char subCadena[TAMANO_CADENA+1];
char caracterBuscar;
char *p;
int indice;
int contador;
cout << "Introduzca una cadena
: ";
cin.getline(cadenaPrincipal, TAMANO_CADENA);
cout << "Introduzca una subcadena de bsqueda: ";
cin.getline(subCadena, TAMANO_CADENA);
cout << "Introduzca un caracter de bsqueda : ";
cin >> caracterBuscar;
cout << endl;
cout << "
1
2
3
4" << endl;
cout << "01234567890123456789012345678901234567890" << endl;
cout << cadenaPrincipal << endl << endl;
cout << "Buscando la cadena " << subCadena << endl;
p = strstr(cadenaPrincipal, subCadena);
contador = 0;
while (p)
{
contador++;
indice = p - cadenaPrincipal;
cout << "Localizado en el ndice " << indice << endl;
p = strstr(++p, subCadena);
}//Fin de while
if (contador == 0)
cout << "No se localiz la subcadena en la cadena principal" << endl;
cout << "\nBuscando el caracter " << caracterBuscar << endl;
p = strchr(cadenaPrincipal, caracterBuscar);

APUNTADORES Y CADENA LECCIN 20

20-63

MIGUEL . TOLEDO MARTNEZ

contador = 0;
while (p)
{
contador++;
indice = p - cadenaPrincipal;
cout << "Localizado en el ndice " << indice << endl;
p = strchr(++p, caracterBuscar);
}//Fin de while
if (contador == 0)
cout << "No localizado el caracter en la cadena principal" << endl;
}//Fin de main()

PENSANDO EN OBJETOS: Iteraciones entre los objetos


sta es la ltima de las tareas de diseo orientado a objetos antes de iniciar el estudio de la programacin
orientada a objetos en otro curso. Despus de completar esta tarea, estar preparado (y probablemente ansioso) para
comenzar a codificar su simulador del elevador. Para completar el simulador del elevador como se defini,
necesitar las tcnicas de C++ de otro curso.
En esta seccin nos concentraremos en las interacciones entre los objetos. Esperamos que le sea de ayuda
para armar el cuadro. Probablemente har adiciones a la lista de objetos, atributos y comportamientos que antes
fueron desarrollados.
Hemos aprendido que la mayora de los objetos en C++ no hacen las cosas de manera espontnea. En
cambio, los objetos responden a estmulos, que vienen en forma de mensajes, los cuales de hecho son llamadas de
funcin que invocan las funciones miembro de los objetos.
Consideremos varias de las interacciones entre las clases de la simulacin del elevador. El planteamiento
del problema indica la persona oprime el botn que se encuentra en ese piso. El sujeto del enunciado es una
persona y el objeto es el botn. ste es un ejemplo de interaccin entre clases. Un objeto de la clase persona enva
un mensaje a un objeto botn. A ese mensaje lo llamamos oprimirBotn. Antes hemos hecho que el mensaje fuera
una funcin miembro de la clase botn.
En este punto, bajo Otros hechos de cada una de las clases de su simulacin, todo lo que ha debido poner
han sido las interacciones entre las clases. Algunas de ellas muestran explcitamente las interacciones entre los
objetos de clase. Pero considere la oracin
"una persona espera a que se abra la puerta del elevador"
Anteriormente hemos listados los dos comportamientos de la puerta del elevador, abrirPuerta y
cerrarPuerta. Pero ahora queremos determinar qu clase de objetos envan estos mensajes. No se indican
explcitamente en la oracin entrecomillada anterior. As que le damos algunas vueltas a esto y nos damos cuenta de
que el elevador mismo enva el mensaje a la puerta. Estas interacciones entre los objetos de clase estn implcitas en
el planteamiento del problema.
Ahora contine refinando la seccin Otros hechos de las distintas clases del simulador del elevador. Estas
secciones ahora debern contener principalmente las interacciones entre las clases. Vea cada una de ellas como:
1.
2.
3.

un objeto de clase de envo particular


que enva un mensaje particular
a un objeto de clase de recepcin particular

Bajo cada clase, aada la seccin Mensajes enviados a los objetos de otras clases (a tales mensajes se les
llama colaboraciones; a partir de ahora emplearemos este trmino) y liste las interacciones entre las clases que
quedan, es decir, bajo la clase persona, incluya la entrada :
APUNTADORES Y CADENA LECCIN 20

20-64

MIGUEL . TOLEDO MARTNEZ

la persona le enva el mensaje oprimirBotn al botn de ese piso


Bajo la clase botn, bajo Colaboraciones, coloque el mensaje:
el botn le enva el mensaje venPorM al elevador
A medida que aada estas entradas, podr agregar atributos y comportamientos a sus objetos. Esto es
perfectamente natural.
Cuando complete este ejercicio de laboratorio, tendr una lista razonablemente completa de las clases que
tendr que implementar su simulador del elevador. Y por cada clase tendr una lista razonablemente completa de sus
atributos y comportamientos, as como de los mensajes que los objetos de dicha clase envan a los objetos de otras
clases
En el curso de la especialidad, estudiar la programacin orientada a objetos en C++. Aprender cmo
crear nuevas clases. Estar listo para escribir en C++ una parte importante del simulador de elevador. Conforme
avance en sus conocimientos habr aprendido lo suficiente para implementar un simulador operativo. Finalmente
aprender a valerse de la herencia para explotar los puntos comunes entre las clases y minimizar la cantidad de
software que necesitar escribir para resolver un problema.
Hagamos un resumen del proceso de diseo orientado a objetos que hemos presentado:
Escriba el planteamiento del problema en un archivo de texto.
Descarte la informacin irrelevante.
Extraiga todos los hechos. Disponga cada hecho en una lnea independiente de un archivo de hechos.
Busque los sustantivos en los hechos; con gran probabilidad sern muchas de las clases que necesitar.
Por cada clase, ponindola en primer nivel, escriba una lista de resumen.
5. Ponga cada hecho en el segundo nivel de la lista, bajo la clase apropiada. Si un hecho menciona varias
clases (como suceder en muchos casos), pngalo bajo todas ellas.
6. Ahora refine el conjunto de hechos bajo cada una de las clases. Liste tres subencabezados bajo cada
clase: Atributos, Comportamientos y Colaboraciones.
7. Bajo Atributos liste la informacin asociada con cada clase.
8. Bajo Comportamientos liste las acciones que pueden llevar a cabo los objetos de dicha clase en respuesta
a la recepcin de un mensaje. Cada comportamiento es una funcin miembro de la clase.
9. Bajo Colaboraciones liste los mensajes que los objetos de esta clase envan a los objetos de otras clases y
las clases que reciben estos mensajes.
10. En este punto su diseo probablemente tendr todava algunas piezas faltantes, que tal vez queden claras a
medida que proceda con la implementacin de su simulador en C++ conforme sus conocimientos de la
programacin orientada a objetos sean ms amplios.
1.
2.
3.
4.

Los atributos y comportamientos con frecuencia se llaman responsabilidades de una clase. La


metodologa de diseo que hemos perfilado aqu a veces se llama clases, responsabilidades y colaboraciones, o
simplemente CRC.

ERRORES COMUNES DE PROGRAMACIN


1.

2.

3.
4.
5.
6.

Suponer que el * con el que se declara un apuntador se extiende a todos los nombres de variables de una lista de
variables de apuntador separada por comas puede causar que tales apuntadores se declaren como no
apuntadores. Cada apuntador debe declararse con un * como prefijo del nombre.
Desreferenciar un apuntador que no ha sido inicializado de manera apropiada o al que no se le ha indica- do
que apunte a una localidad especfica de memoria, puede producir un error fatal de tiempo de ejecucin o
modificar accidentalmente informacin importante, permitiendo que el programa se ejecute hasta el final, lo que
arrojar resultados incorrectos.
El intento por desreferenciar un no apuntador es un error de sintaxis.
Desreferenciar un apuntador 0 por lo general causa un error fatal en tiempo de ejecucin.
Es un error no desreferenciar un apuntador cuando es necesario hacerlo con el fin de obtener el valor al que
apunta el apuntador.
No inicializar un apuntador que se ha declarado corno const es un error de sintaxis.

APUNTADORES Y CADENA LECCIN 20

20-65

MIGUEL . TOLEDO MARTNEZ

7.
8.
9.
10.
11.
12.
13.
14.

15.
16.
17.

18.
19.
20.
21.

22.

23.

El empleo del operador sizeof en una funcin con el fin de determinar el tamao en bytes de un parmetro de
arreglo da como resultado el tamao en bytes del apuntador, no el tamao en bytes del arreglo.
La omisin de los parntesis en una operacin sizeof cuando el operando es un nombre de tipo es un error de
sintaxis.
La utilizacin de aritmtica de apuntadores sobre un apuntador que no hace referencia a un arreglo de valores
normalmente es un error de lgica.
La resta o comparacin de dos apuntadores que no hacen referencia a elementos del mismo arreglo por lo
general es un error de lgica.
Salirse de cualquiera de los extremos de un arreglo al aplicar aritmtica de apuntadores normalmente es un
error de lgica.
La asignacin de un apuntador de un tipo a otro apuntador de un tipo distinto (que no sea void *) sin convertir el
primer apuntador al tipo del segundo es un error de sintaxis.
Desreferenciar un apuntador void * es un error de sintaxis.
Aunque los nombre de arreglos son apuntadores al inicio de stos y aunque los apuntadores se pueden modificar
con expresiones aritmticas, los nombres de los arreglos no pueden modificarse mediante expresiones
aritmticas, dado que son apuntadores constantes.
Es un error no asignar suficiente espacio en un arreglo de caracteres para almacenar el carcter nulo de
terminacin de la cadena.
Es un error crear o utilizar una cadena que no contenga un carcter nulo de terminacin.
El procesamiento de un solo carcter como cadena puede generar un error fatal en tiempo de ejecucin. Una
cadena es un apuntador -probablemente sea un entero de tamao respetablemente grande. Sin embargo, un
carcter es un entero pequeo (los valores ASCII van de 0 a 255) En muchos sistemas, esto provoca un error,
pues las direcciones de memoria bajas se reservan para fines especiales, como controladores de interrupciones
del sistema operativo -por lo tanto, suceden violaciones de acceso.
Si pasa un carcter como argumento a una funcin cuando se espera una cadena, puede causar un error fatal
en tiempo de ejecucin.
Pasar una cadena como argumento de una funcin cuando se espera un carcter es un error de sintaxis.
Es un error olvidar incluir el archivo de encabezado <string.h> cuando se emplean funciones de la biblioteca
de manejo de cadenas.
No agregar un carcter nulo de terminacin al primer argumento de strncpy cuando el tercer argumento es
menor o igual que la longitud de la cadena del segundo argumento puede ser causa de errores fatales en tiempo
de ejecucin.
La suposicin de que strcmp y strncmp devuelven 1 cuando sus argumentos son iguales es un error de lgica.
Ambas funciones devuelven 0 (que es el valor de falso de C++) cuando hay una igualdad. Por lo tanto, al probar
la igualdad de dos cadenas, el resultado de la funcin strcmp o strncmp debe compararse con 0 para determinar
si son iguales.
Es un error no notar que strtok modifica la cadena que se est dividiendo en tokens y luego intentar utilizarla
como si fuera la cadena original, sin modificaciones.

BUENAS PRCTICAS DE PROGRAMACIN


1.
2.

3.
4.
5.

Aunque no se requiere, la inclusin de las letras Ptr en los nombres de variables de apuntador deja claro que
dichas variables son apuntadores y que necesitan manipularse como tales.
Utilice una llamada por valor para pasar argumentos a una funcin, a menos que el invocador requiera
explcitamente que la funcin llamada modifique el valor de la variable argumento en el entorno del invocador.
ste es otro ejemplo del principio de menor privilegio.
Antes de utilizar una funcin, revise su prototipo de funcin a fin de determinar los parmetros que puede
modificar.
Utilice la notacin de arreglos en lugar de la notacin de apuntadores cuando manipule arreglos. Aunque la
compilacin del programa tal vez tarde un poco ms, con seguridad ser ms claro.
Al almacenar una cadena de caracteres en un arreglo de caracteres, debe asegurarse de que el arreglo es lo
bastante grande para contener la cadena ms grande que almacenar. C++ permite el almacenamiento de
cadenas de cualquier longitud. Si una cadena es ms grande que el arreglo en el que la almacenar, los
caracteres que estn despus del final del arreglo sobrescribirn la informacin en memoria que se encuentre a
continuacin de dicho arreglo.

APUNTADORES Y CADENA LECCIN 20

20-66

MIGUEL . TOLEDO MARTNEZ

PROPUESTAS DE DESEMPEO
1.

2.
3.

Los objetos grandes, como las estructuras, se deben pasar mediante apuntadores hacia datos constantes o con
referencias hacia datos constantes, con el fin de lograr los beneficios en desempeo de la llamada por referencia
y la seguridad de la llamada por valor.
sizeof es un operador unario de tiempo de compilacin, no una funcin de tiempo de ejecucin. Por lo tanto,
la utilizacin de sizeof no afecta negativamente el desempeo en tiempo de ejecucin.
A veces los algoritmos que emergen de manera natural pueden tener sutiles problemas de desempeo, como el
aplazamiento indefinido. Es importante buscar algoritmos que eviten el aplazamiento indefinido.

PROPUESTAS DE PORTABILIDAD
1.

2.
3.

4.

5.

6.
7.

El formato en el que se enva a la salida un apuntador depende de la mquina. Algunos sistemas envan a la
salida los valores de apuntador corno enteros hexadecimales, mientras que otros lo hacen como enteros
decimales.
Aunque const est bien definido en el ANSI C y C++, algunos compiladores no lo aplican adecuadamente.
El nmero de bytes que se utilizan para almacenar un tipo de datos particular puede variar segn el sistema. Al
escribir programas que dependan de los tamaos de los tipos de datos y que se ejecutarn en varios sistemas de
cmputo, hay que utilizar sizeof para determinar el nmero de bytes en que se almacenan los distintos tipos de
datos.
La mayora de las computadoras actuales tienen enteros de 2 o 4 bytes. Algunas de las ms nuevas utilizan
enteros de 8 bytes. Debido a que los resultados de la aritmtica de apuntadores dependen del tamao de los
objetos a los que apunta un apuntador, la aritmtica de apuntadores depende de la mquina.
Cuando se inicializa una variable de tipo char * con una literal de cadena, algunos compiladores pueden poner
dicha cadena en alguna localidad de memoria en la que no puede ser modificado. Si necesitara modificar una
literal de cadena, deber almacenarla en un arreglo de caracteres, a fin de asegurar la posibilidad de modificarla
en todos los sistemas.
Los cdigos numricos empleados para la representacin de caracteres pueden cambiar segn la computadora.
No use los cdigos ASCII especficos, como en el caso de if ( ch == 65 ) ms bien, emplee la constante del
carcter, como en if ( ch = = A ).

OBSERVACIONES DE INGENIERA DE SOFTWARE


1.

2.
3.

4.
5.

El calificador const puede servir para aplicar el principio de menor privilegio. La utilizacin del principio de
menor privilegio para disear software de la manera correcta puede reducir en gran medida el tiempo de
depuracin y los efectos secundarios, as como simplificar la modificacin y el mantenimiento de los
programas.
Si un valor no cambia (o no debe cambiar) en el cuerpo de una funcin a la cual se pasa, el parmetro debe ser
declarado como const con el fin de asegurar que no sea modificado por accidente.
Slo es posible alterar un valor en un invocador cuando se efecta una llamada por valor. Este valor debe
asignarse a partir del valor de devolucin de la funcin. Si hay que modificar varios valores en un invocador, es
necesario pasar varios argumentos mediante llamada por referencia.
La colocacin de prototipos de funcin en las definiciones de otras funciones aplica el principio de menor
privilegio, restringiendo las llamadas de funcin a las funciones en las que aparecen los prototipos.
Al pasar un arreglo a una funcin, tambin hay que pasar su tamao (en lugar de integrarlo en la funcin)
Esto contribuye a hacer ms general la funcin. Las funciones generales con frecuencia son reutilizables en
otros programas.

INDICACIONES DE PRUEBA Y DEPURACIN


1.

Los apuntadores se deben inicializar con el fin de evitar que apunten a reas desconocidas o no inicializadas de
la memoria.

APUNTADORES Y CADENA LECCIN 20

20-67

MIGUEL . TOLEDO MARTNEZ

LO QUE NECESITA SABER


Antes de continuar con la siguiente leccin, asegrese de haber aprendido lo siguiente:
!"Los apuntadores son variables que contienen como valor la direccin de otras variables.
!"La declaracin:
int *ptr;
declara que ptr es un apuntador a un objeto de tipo int, y se lee ptr es un apuntador a int. El * como se
emplea en una declaracin, indica que la variable es un apuntador.
!"Hay tres valores que se pueden utilizar para inicializar un apuntador: 0, NULL o la direccin de un objeto
del mismo tipo. Es lo mismo inicializar un apuntador a 0 que a NULL.
!"El nico entero que se puede asignar a un apuntador es 0.
!"El operador & (de direccin) devuelve la direccin de su operando.
!"El operando del operador de direccin debe ser un nombre de variable; el operador de direccin no se
puede aplicar a constantes, a expresiones que no devuelvan una referencia ni a variables declaradas con la
clase de almacenamiento register.
!"El operador *, conocido como operador de indireccin o de desreferencia, devuelve un sinnimo, alias o
apodo del nombre del objeto al que apunta su operando en memoria. A esto se le llama desreferenciar el
apuntador.
!"Al llamar una funcin con un argumento que el invocador desea que la funcin llamada modifique, debe
pasarse la direccin del argumento. La funcin llamada modifica entonces el valor del argumento del
invocador mediante el operador de indireccin (*)
!"Una funcin que recibe como argumento una direccin debe incluir un apuntador corno su parmetro
correspondiente.
!"No es necesario incluir los nombres de los apuntadores en los prototipos de funcin; slo es necesario
incluir los tipos de apuntador. Los nombres de apuntador se pueden agregar con fines de documentacin,
pero el compilador los ignora.
!"El calificador const le permite al programador informarle al compilador que no se deber modificar el
valor de una variable en particular.
!"Si se intenta modificar un valor const, el compilador lo detecta y emite un aviso o un error, segn el
compilador de que se trate.
!"Hay cuatro maneras de pasar un apuntador a una funcin: mediante un apuntador no constante hacia
datos no constantes, por medio de un apuntador no constante hacia datos constantes, utilizando un
apuntador constante hacia datos no constantes y a travs de un apuntador constante hacia datos constantes.
!"Los arreglos se pasan de manera automtica por referencia, utilizando apuntadores, pues el valor del
nombre del arreglo es la direccin del mismo.
!"Para pasar un solo elemento de un arreglo como llamada por referencia mediante apuntadores, es
necesario pasar la direccin del elemento especfico del arreglo.
!"C++ ofrece el operador unario especial sizeof, que determina el tamao de un arreglo (o de cualquier otro
tipo de datos) en bytes durante la compilacin del programa.
!"Cuando se aplica al nombre de un arreglo, el operador sizeof devuelve, como entero, la cantidad total de
bytes que hay en ella.
!"El operador sizeof se puede aplicar a cualquier nombre de variable, tipo o constante.
!"Las operaciones aritmticas que se pueden llevar a cabo sobre los apuntadores son incremento (++),
decremento (--), suma (+ o + =) o resta (- o - =) de un apuntador y un entero, y resta de un apuntador a
otro.
!"Cuando se suma o resta un entero a un apuntador, el apuntador se incremento o decrementa por dicho
entero, multiplicado por el tamao del objeto al que se apunta.
!"Las operaciones de aritmtica de apuntadores slo deben efectuarse sobre partes contiguas de la
memoria, por ejemplo, sobre un arreglo. Todos los elementos de un arreglo se almacenan en localidades
contiguas de memoria.

APUNTADORES Y CADENA LECCIN 20

20-68

MIGUEL . TOLEDO MARTNEZ

!"Al llevar a cabo aritmtica de apuntadores sobre un arreglo de caracteres, el resultado es como el de la
aritmtica normal, pues cada carcter se almacena en un byte de memoria.
!"Se puede asignar un apuntador a otro si ambos son del mismo tipo. De otra manera, debe aplicarse una
conversin mediante cast. La excepcin a esto es un apuntador a void, que es un tipo de apuntador general
que puede contener apuntadores de cualquier tipo. A los apuntadores a void se les puede asignar
apuntadores de otros tipos. Es posible asignar un apuntador void a un apuntador de otro tipo slo a travs
de una conversin mediante cast explcita.
!"No se puede desreferenciar un apuntador a void.
!"Los apuntadores se pueden comparar por medio de los operadores de igualdad y relacionales. Las
comparaciones de apuntadores por lo general slo tienen sentido si los apuntadores apuntan a miembros del
mismo arreglo.
!"Los apuntadores se pueden indexar de la misma manera que los nombres de arreglos.
!"Un nombre de arreglo es equivalente a un apuntador al primer elemento del mismo.
!"En la notacin de apuntador/desplazamiento, el desplazamiento es igual a un ndice de arreglo.
!"Todas las expresiones de arreglos indexadas se pueden escribir con un apuntador y un desplazamiento,
utilizando corno apuntador el nombre del arreglo o un apuntador separado que apunte a dicho arreglo.
!"Un nombre de arreglo es un apuntador constante que siempre apunta a la misma localidad de memoria.
!"Es posible tener arreglos de apuntadores.
!"Un apuntador a una funcin es la direccin donde reside el cdigo de la funcin.
!"Los apuntadores a funciones pueden ser pasados a otras funciones, devueltos de otras funciones,
almacenados en arreglos y asignados a otros apuntadores.
!"Un uso comn de los apuntadores de funcin es en los llamados sistemas operados por men. Los
apuntadores de funcin sirven para seleccionar la funcin a llamar segn un elemento particular del men.
!"La funcin strcpy copia su segundo argumento (una cadena) a su primer argumento (un arreglo de
caracteres) El programador debe asegurarse de que el arreglo es lo bastante grande para almacenar la
cadena y su carcter nulo de terminacin.
!"La funcin strncpy es equivalente a strcpy, excepto que una llamada a strncpy especifica el nmero de
caracteres a copiar de la cadena al arreglo. El carcter nulo de terminacin slo se copiar si el nmero de
caracteres a copiar es de uno ms que la longitud de la cadena.
!"La funcin strcat aade la cadena de su segundo argumento (incluyendo el carcter nulo de terminacin) a
la cadena de su primer argumento. El primer carcter de la segunda cadena reemplaza el carcter nulo ( \0
) de la primera cadena. El programador debe asegurarse de que el arreglo en el que est la primera cadena
es lo bastante grande para almacenar tanto la primera cadena como la segunda.
!"La funcin strncat agrega un nmero de caracteres especificado de la segunda cadena a la primera. Se
aade un carcter nulo de terminacin al resultado.
!"La funcin strcmp compara la cadena de su primer argumento con la cadena de su segundo argumento,
carcter por carcter. La funcin devuelve 0 si ambas son iguales, un resultado negativo si la primera es
menor que la segunda y un valor positivo si la primera es mayor que la segunda.
!"La funcin strncmp es equivalente a strcmp, excepto que strncmp compara un nmero especificado de
caracteres. Si el nmero de caracteres de una cadena es menor que el nmero especificado, strncmp
compara los caracteres hasta que se encuentra el carcter nulo en la cadena ms corta.
!"Una secuencia de llamadas a strtok divide una cadena en tokens separados por los caracteres contenidos en
el segundo argumento. La primera llamada contiene como primer argumento la cadena a dividir en tokens,
y las llamadas siguientes que continan dividiendo la misma cadena en tokens contienen NULL como
primer argumento. Cada llamada devuelve un apuntador al token actual. Si no hay ms tokens al llamar a
strtok, se devuelve NULL.
!"La funcin strlen toma como argumento una cadena y devuelve el nmero de caracteres que hay en ella; en
la longitud de la cadena no se incluye el carcter nulo de terminacin.
!"Una cadena de caracteres es un arreglo de caracteres que terminan por los caracteres ASCII 0 o NULL.
!"Puede crear una cadena de caracteres dentro de su programa mediante la declaracin de un arreglo de tipo
char.
!"Su programa es responsable de agregar el carcter NULL despus del ltimo carcter de la cadena.
APUNTADORES Y CADENA LECCIN 20

20-69

MIGUEL . TOLEDO MARTNEZ

!"Cuando su programa utiliza cadenas constantes entre comillas, el compilador C++ automticamente
agrega el carcter NULL.
!"C++ le permite inicializar las cadenas al declararlas, especificando los caracteres deseados entre comillas.
!"La mayora de los compiladores C++ proporcionan un conjunto de funciones para el manejo de cadenas en
las libreras en tiempo de ejecucin.

PREGUNTAS Y PROBLEMAS
PREGUNTAS
1.

Llene los siguientes espacios en blanco:


a) Un apuntador es una variable que contiene como valor la ______________ de otra variable.
b) Los tres valores que se pueden utilizar para inicializar un apuntador son __________, _________ y
____________.
c) El nico entero que se puede asignar a un apuntador es________________

2.

Indique si las siguientes oraciones son verdaderas o falsas. Si la respuesta es falsa, explique por qu.
a) El operador de direccin & slo puede aplicarse a constantes, a expresiones y a variables declaradas
con la clase de almacenamiento register.
b) Se puede desreferenciar un operador declarado como void.
c) No es posible asignar un apuntador de un tipo a otro de otro tipo sin una operacin de conversin
mediante cast.

3.

Responda las siguientes preguntas. Suponga que los nmeros de punto flotante de precisin sencilla se
almacenan en 4 bytes y que la direccin de inicio del arreglo est en la localidad 1002500 de la memoria. Cada
parte del ejercicio debera utilizar el resultado de las partes previas, donde aplique.
a) Declare un arreglo de tipo float llamado numeros que tenga 10 elementos, los cuales deber inicializar a
los valores 0.0, 1.1, 2.2, ..., 9.9. Suponga que la constante simblica TAMANO se ha definido como10.
b) Declare el apuntador nPtr que apunta a un objeto de tipo float.
c) Imprima los elementos del arreglo numeros utilizando la notacin de ndices de arreglos. Emplee una
estructura for y suponga que se ha declarado la variable de control entera i. Imprima cada nmero con
una posicin de precisin a la derecha del punto decimal.
d) D dos instrucciones independientes que asignen la direccin de inicio del arreglo numeros a la variable
de apuntador nPtr.
e) Imprima los elementos del arreglo numeros por medio de notacin de apuntador/desplazamiento con el
apuntador nPtr.
f) Imprima los elementos del arreglo numeros utilizando notacin de apuntador/desplazamiento con el
nombre del arreglo como apuntador.
g) Imprima los elementos del arreglo numeros indexando el apuntador nPtr.
h) Haga referencia al elemento 4 del arreglo numeros empleando notacin de ndices de arreglos, notacin
de apuntador/desplazamiento con el nombre del arreglo como apuntador, notacin de ndice de apuntador
con nPtr y notacin de apuntador/desplazamiento con nPtr.
i) Suponiendo que nPtr apuntara al inicio del arreglo numeros, a qu direccin hace referencia nPtr + 8?
Qu valor se almacena en dicha localidad?
j) Suponiendo que nPtr apunte a numeros[5], qu direccin referencia nPtr despus de que se ejecuta nPtr
-= 4?. Qu valor se almacena en esa localidad?

4.

Escriba una sola instruccin que lleve a cabo la tarea indicada para cada uno de los siguientes casos. Suponga
que ya se han declarado las variables de punto flotante numero1 y numero2, y que numero1 se ha inicializado a
7.3. Tambin suponga que la variable ptr es de tipo char * y que los arreglos s1[100] s2[100] son de tipo char.
a)
b)
c)
d)

Declare la variable fPtr como apuntador a un objeto de tipo float.


Asgnele a la variable de apuntador fPtr la direccin de la variable numero1.
Imprima el valor del objeto al que apunta fPtr.
Asgnele a la variable numero2 el valor del objeto al que apunta fPtr.

APUNTADORES Y CADENA LECCIN 20

20-70

MIGUEL . TOLEDO MARTNEZ

e)
f)
g)
h)
i)
j)
k)
l)
5.

Imprima el valor de numero2.


Imprima la direccin de numero1.
Imprima la direccin almacenada en fPtr. El valor que se imprime es igual a la direccin de numero1?
Copie la cadena almacenada en el arreglo s2 al arreglo s1.
Compare la cadena en s1 con la cadena en s2. Imprima el resultado.
Agregue 10 caracteres de la cadena de s2 a la cadena de s1.
Determine la longitud de la cadena de s1. Imprima el resultado.
Asgnele a ptr la localidad del primer token de s2. Los tokens de s2 se separan mediante comas (,).

Haga lo siguiente:
a) Escriba el encabezado de una funcin llamada intercambio(), que toma como parmetros los apuntadores
a los nmeros de punto flotante x e y y que no devuelve nada.
b) Escriba el prototipo de la funcin de la parte (a).
c) Escriba el encabezado de una funcin llamada evaluar(), que devuelve un entero y toma como parmetros
el entero x y un apuntador a la funcin poly. sta toma un parmetro entero y devuelve un entero.
d) Escriba el prototipo de funcin de la parte (c)
e) Muestre dos mtodos diferentes para inicializar el arreglo de caracteres vocal con la cadena de vocales
AEIOU.

6.

Encuentre los errores en los siguientes segmentos de programa. Suponga que


int *zPtr;
// zPtr referenciar el arreglo z
int *aPtr = 0;
void *sPtr = 0;
int numero, i;
int z[5] = { 1, 2, 3, 4, 5}
sPtr = z;
a) ++zPtr;
b) // Utiliza el apuntador para obtener el primer valor del arreglo numero = zPtr;
c) // le asigna a numero el elemento 2 del arreglo (el valor 3)
a. numero = *zPtr[ 2];
d) // imprime todo el arreglo z
a. for ( i = 0; i <= 5; i++ )
i. cout << zPtr[ i ] << endl;
e) // le asigna a numero el valor al que apunta sPtr
f) numero = *sPtr;
g) ++z;
h) char s[10];
i) cout << strncpy( s, adios, 5) << endl;
j) char s[12];
k) strcpy( s, Bienvenido a casa);
l) if( strcmp( cadena1, cadena2))
cout << Las cadenas son iguales << endl;

7.

Qu imprimen las siguientes instrucciones cuando se ejecutan (si es que imprimen algo)? Si alguna de estas
contiene un error, descrbalo e indique la manera de corregirlo. Suponga las siguientes declaraciones de
variables:
char s1[50] = jack, s2[50] = jill, s3[50], *sptr;
a)
b)
c)
d)

cout << strcpy(s3, s2) << endl;


cout << strcat(strcat(strcpy(s3, s1), and ), s2) << endl;
cout << strlen(s1) + strlen(s2) << endl;
cout << strlen(s3) << endl;

APUNTADORES Y CADENA LECCIN 20

20-71

MIGUEL . TOLEDO MARTNEZ

8.

Indique si las siguientes oraciones son verdaderas o falsas. Si la respuesta es falsa, explique por qu.
a) La comparacin de dos apuntadores que apuntan a arreglos diferentes no tiene sentido.
b) Debido a que el nombre de un arreglo es un apuntador al primer elemento de dicho arreglo, los nombres
de arreglo deben manipularse de la misma manera que los apuntadores.

9.

Conteste a lo siguiente. Suponga que los enteros sin signo se almacenan en 2 bytes y que la direccin inicial del
arreglo es la localidad 1002500 de la memoria.
a) Declare un arreglo de tipo unsigned int llamado valores que cuente con 5 elementos, los cuales deber
inicializar a los enteros pares del 2 al 10. Suponga que la constante simblica se ha definido como 5.
b) Declare el apuntador vPtr que apunta a un objeto de tipo unsigned int.
c) Imprima los elementos del arreglo valores utilizando notacin de ndices de arreglo. Emplee una
estructura for y suponga que se ha declarado la variable de control entera i.
d) Escriba dos instrucciones separadas que asignen la direccin inicial del arreglo valores a la variable de
apuntador vPtr.
e) Imprima los elementos del arreglo valores utilizando notacin de apuntador/desplazamiento.
f) Imprima los elementos del arreglo valores utilizando notacin de apuntador/desplazamiento con el
nombre del arreglo como apuntador.
g) Imprima los elementos del arreglo valores indexando el apuntador al arreglo.
h) Haga referencia al elemento 5 de valores utilizando notacin de ndices de arreglo, notacin de
apuntador/desplazamiento con el nombre del arreglo como apuntador, notacin de ndices de apuntador y
notacin de apuntador/desplazamiento.
i) A qu direccin hace referencia vPtr + 3? Qu valor se almacena en dicha localidad?
j) Suponiendo que vPtr apunte a valores[4], a qu direccin hace referencia vPtr -= 4? Qu valor se
almacena en dicha localidad?

10. Para cada uno de los siguientes puntos, escriba una instruccin que lleve a cabo la tarea indicada. Suponga que
se han declarado las variables enteras largas valor1 y valor2, y que valor1 se ha inicializado a 200000.
a)
b)
c)
d)
e)
f)
g)

Declare la variable lPtr como apuntador a un objeto de tipo long.


Asigne la direccin de la variable valor1 a la variable de apuntador lPtr.
Imprima el valor del objeto al que apunta lPtr.
Asgnele a la variable valor2 el valor del objeto al que apunta lPtr.
Imprima el valor de valor2.
Imprima la direccin de valor1.
Imprima la direccin almacenada en lPtr. Es igual el valor impreso que la direccin de valor1?

11. Haga lo siguiente.


a) Escriba el encabezado de la funcin cero, que toma como parmetro el arreglo de enteros largos
enterosLargos y no devuelve nada.
b) Escriba el prototipo de la funcin de la parte (a)
c) Escriba el encabezado de la funcin agrega1YSuma, que toma como parmetro el arreglo de enteros
demasiadoPequeno y devuelve un entero.
d) Escriba el prototipo de la funcin descrita en la parte (c)

APUNTADORES Y CADENA LECCIN 20

20-72

MIGUEL . TOLEDO MARTNEZ

PROBLEMAS
Nota: Los problemas 1 a 4 son algo complejos. Una vez que los haya hecho, debera poder implementar con
facilidad los juegos de naipes ms comunes.

1.

Modifique el programa BARAJAS.CPP, para que la funcin de barajado de los naipes reparta una mano de
pquer de cinco naipes. Despus escriba las funciones que realicen lo siguiente:
a)
b)
c)
d)
e)
f)

Determine si la mano contiene un par.


Determine si la mano contiene dos pares.
Determine si la mano contiene una tercia (por ejemplo, tres sotas)
Determine si la mano contiene un pquer (por ejemplo, cuatro ases)
Determine si la mano contiene un flux (es decir, los cinco naipes del mismo palo)
Determine si la mano contiene una corrida (es decir, cinco naipes del mismo palo con valores
consecutivos)

2.

Utilice las funciones desarrolladas en el problema 1 para escribir un programa que reparta dos manos de
pquer de cinco naipes, las evale y determine cul es la mejor.

3.

Modifique el problema desarrollado en el problema 2 de modo que simule al repartidor. La mano del repartidor
se baraja cerrada, para que el jugador no la pueda ver. El programa deber evaluar dicha mano y, con base en
su calidad, cambiar las inservibles de la mano original por uno, dos o tres naipes. El programa deber reevaluar
la mano del repartidor. (Precaucin: ste es un problema complicado)

4.

Modifique el programa desarrollado en el problema 3 para que pueda manejar automticamente la mano del
repartidor, pero que le permita al jugador decidir cules de los naipes de su propia mano cambiar. El programa
deber evaluar ambas manos y decidir quin gana. Ahora utilice este nuevo programa para jugar 20 juegos
contra la computadora. Quin gana ms juegos, usted o la computadora? Haga que uno de sus amigos
juegue 20 juegos contra la computadora. Quin gana ms juegos? Con base en estos resultados, haga las
modificaciones necesarias para refinar el programa de pquer (ste tambin es un problema difcil) Juegue 20
juegos ms. El programa modificado juega mejor?

5.

En el programa de barajado y reparticin de naipes BARAJAS.CPP, utilizamos intencionalmente un algoritmo


de barajado ineficiente que present la posibilidad de aplazamiento indefinido. En este problema, crear un
algoritmo de barajado de alto desempeo que evite el aplazamiento indefinido.
Modifique el programa BARAJAS.CPP, como sigue. Inicialice el arreglo paquete como se muestra en la
figura 20.10 . Modifique la funcin barajar() para que haga un ciclo fila por fila y columna por columna a
travs del arreglo, tocando una vez todos los elementos. Cada elemento debe intercambiarse con otro elemento
del arreglo seleccionado al azar. Imprima el arreglo resultante para determinar si se baraj de manera adecuada
(como en la figura 20.11, por ejemplo) Tal vez desee que el programa llame varias veces a la funcin barajar(),
a fin de asegurar un barajado satisfactorio.
Note que, aunque este enfoque mejor el algoritmo de barajado, ste an tiene que buscar en el arreglo paquete
el naipe 1, luego el 2, el 3, etc. Es ms, an despus de que el algoritmo de barajado encuentra y reparte el
naipe, sigue buscando en el resto de los naipes. Modifique el programa BARAJAS.CPP para que, una vez
repartido un naipe, no se hagan mas intentos por encontrarlo, procediendo de inmediato a repartir el siguiente.

Arreglo paquete sin barajar


0
1
2
3

0
1
14
27
40

1
2
15
28
41

2
3
16
29
42

3
4
17
30
43

4
5
18
31
44

5
6
19
32
45

6
7
20
33
46

7
8
21
34
47

8
9
22
35
48

9
10
23
36
49

10
11
24
37
50

11
12
25
38
51

12
13
26
39
52

Figura 20.10. Arreglo paquete sin barajar

APUNTADORES Y CADENA LECCIN 20

20-73

MIGUEL . TOLEDO MARTNEZ

Ejemplo del arreglo paquete sin barajado


0
1
2
3

0
19
13
12
50

1
40
28
33
38

2
27
14
15
52

3
25
16
42
39

4
36
21
43
48

5
46
30
23
51

6
10
8
45
9

7
34
11
3
5

8
35
31
29
37

9
41
17
32
49

10
18
24
4
22

11
2
7
47
6

12
44
1
26
20

Figura 20.11. Ejemplo del arreglo paquete sin barajado


6.

(Simulacin: la tortuga y la liebre) En este problema, recrear la carrera clsica de la tortuga y la liebre. Se
valdr de la generacin de nmeros aleatorios para desarrollar la simulacin de este memorable evento.
Nuestros contendientes comienzan la carrera en el cuadro 1 de una serie de 70 cuadros. Cada cuadro representa
una posicin posible en la ruta de la carrera. La lnea de meta est en el cuadro 70. El primer contendiente que
llegue o pase el cuadro 70 obtiene como recompensa un cubo de zanahorias y lechuga fresca. La ruta sube
serpenteando por la ladera de una montaa resbalosa, por lo que ocasionalmente los contendientes pierden
terreno.
Hay un reloj que pulsa una vez por segundo. Con cada pulso del reloj, el programa deber ajustar la posicin de
los animales, de acuerdo con las siguientes reglas:
Animal

Tipo de movimiento

Tortuga Paso veloz


Resbaln
Paso lento
Liebre
Duerme
Gran salto
Gran resbaln
Salto pequeo
Resbaln pequeo

Porcentaje del tiempo


50%
20%
30%
20%
20%
10%
30%
20%

Movimiento real
3 cuadros a la derecha
6 cuadros a la izquierda
1 cuadro a la derecha
No se mueve
9 cuadros a la derecha
12 cuadros a la izquierda
1 cuadro a la derecha
2 cuadros a la izquierda

Utilice variables para llevar el registro de las posiciones de los animales (las posiciones son de la 1 a la 70)
Cada animal debe comenzar en la posicin 1 (es decir, la puerta de salida) Si un animal resbala hacia la
izquierda, quedando antes del cuadro 1, devulvalo al cuadro 1.
Genere los porcentajes de la tabla previa produciendo un entero aleatorio, i, que est en el rango 1 i 10.
Para la tortuga, efecte un paso veloz cuando 1 i 5, un resbaln cuando 6 i 7 y un paso lento
cuando 8 i 10. Utilice una tcnica similar para mover a la liebre.
Comience la carrera imprimiendo
BANG!
Y ARRANCAN!
Por cada pulso del reloj (es decir, cada repeticin del ciclo), imprima una lnea de 70 posiciones que
muestre la posicin de la tortuga mediante la letra T y la de la liebre mediante la letra L. Ocasionalmente
los contendientes caern en el mismo cuadro. En este caso, la tortuga morder a la liebre y el programa
deber imprimir OUCH! en tal posicin. Todas las posiciones de impresin que no sean T, L ni OUCH!
(en caso de empate), deben estar en blanco.
Despus de la impresin de cada lnea, pruebe si alguno de los animales ha llegado o pasado el cuadro 70.
De ser as, imprima el ganador y termine la simulacin. Si la tortuga gana, imprima LA TORTUGA
GANA! BRAVO! Si la liebre gana, imprima La liebre gana. Ni hablar. Si ambos animales llegan a la
meta con el mismo pulso de reloj, tal vez usted quiera favorecer a la tortuga (el desvalido) o imprimir Es un
empate. Si ninguno de los animales gana, vuelva a efectuar el ciclo, simulando el siguiente pulso del reloj.
APUNTADORES Y CADENA LECCIN 20

20-74

MIGUEL . TOLEDO MARTNEZ

SECCIN ESPECIAL: Construya su propia computadora


En los siguientes problemas nos desviaremos temporalmente del mundo de la programacin con lenguajes
de alto nivel. Abriremos una computadora y veremos su estructura interna. Presentaremos la programacin en
lenguaje de mquina y escribiremos varios programas en dicho lenguaje. Para hacer que esta sea una experiencia
especialmente valiosa, despus construiremos una computadora (por medio de la tcnica de simulacin basada en
software) en la que podr ejecutar sus programas en lenguaje de mquina.
7.

(Programacin en lenguaje de mquina) Crearemos una computadora a la que llamaremos Simpletron. Como
su nombre implica, es una mquina sencilla, pero como pronto veremos, tambin es poderosa. La Simpletron
ejecuta programas escritos en el nico lenguaje que entiende directamente; es decir, el lenguaje de mquina
Simpletron, o SML.
La Simpletron contiene un acumulador -es decir, un registro especial en el que se coloca la informacin antes
de que la Simpletron la tome para efectuar clculos o examinarla de varias maneras. Toda la informa de la
Simpletron se maneja en trminos de palabras. Una palabra es un nmero decimal de cuatro dgitos con
signo, como +3364, -1293, +0007, -0001, etc. La Simpletron viene equipada con una memoria de 100
palabras, las cuales se referencian por su nmero de localidad: 00, 01, ..., 99.
Antes de ejecutar un programa SML, debemos cargar el programa en memoria. La primera instruccin de cada
programa SML siempre queda en la localidad 00. El simulador comenzar la ejecucin a partir de localidad.
Cada instruccin en SML, ocupa una palabra de la memoria de la Simpletron (por lo tanto, las instrucciones
son nmeros decimales de cuatro dgitos con un signo) Supondremos que el signo de una instruccin SML
siempre es positivo, pero el signo de una palabra de datos puede ser positivo o negativo. Cada localidad de la
memoria de la Simpletron puede contener una instruccin, un valor de datos utilizado por el programa o un
rea no utilizada (y, por lo tanto, indefinida) de la memoria. Los primeros dos dgitos de cada instruccin SML
son el cdigo de operacin, que especifica que se llevar a cabo. Los cdigos de operacin de SML aparecen en
la figura 20.12.

Cdigo de operacin

Significado

Operaciones de entrada/salida:
const int READ = 10;

Lee a palabra del teclado y la pone en una localidad


especfica de la memoria

const int WRITE = 11;

Escribe en la pantalla la palabra ubicada en una localidad


especfica de memoria.

Operaciones de carga/almacenamiento:
const int LOAD = 20;

Carga en el acumulador la palabra ubicada en una


localidad especfica de memoria.

Const int STORE = 21;

Almacena la palabra que se encuentra en el


acumulador en una localidad especfica de memoria.
Operaciones aritmticas:

const int ADD = 30;

Suma la palabra ubicada en una localidad especfica


de memoria a la palabra que se encuentra en el
acumulador (deja el resultado en el acumulador).

const int SUBTRACT = 31;

Resta la palabra ubicada en una localidad especfica de


memoria de la palabra que se encuentra en el
acumulador (deja el resultado en el acumulador).

const int DIVIDE = 32;

Divide la palabra ubicada en una localidad especfica


de memoria entre la palabra que se encuentra en el
acumulador (deja el resultado en el acumulador).

const int MULTIPLY = 33;

Multiplica la palabra ubicada en una localidad


especfica de memoria por la palabra que se encuentra

APUNTADORES Y CADENA LECCIN 20

20-75

MIGUEL . TOLEDO MARTNEZ

Cdigo de operacin

Significado

en el acumulador (deja el resultado en el acumulador).


operaciones de transferencia de control:
const int BRANCH = 40;

Bifurca a una localidad especfica de memoria.

const int BRANCHNEG = 41;

Bifurca a una localidad especfica de memoria si el


acumulador es negativo.

const int BRANCHZERO = 41;

Bifurca a una localidad especfica de memoria si el


acumulador es igual a cero.

const int HALT = 43;

Termina, el programa ha completado su trabajo.

Figura 20.12. Cdigos de Operacin del lenguaje de mquina Simpletron


Los ltimos dos dgitos de las instrucciones SML son el operando (la direccin de la localidad especfica
de memoria que contiene la palabra a la que se aplica la operacin)
Ahora consideremos varios programas SML sencillos. El primero (ejemplo 1) lee del teclado dos nmeros
y calcula e imprime su suma. La instruccin +1007 lee el primer nmero y lo pone en la localidad 07 (la
cual ha sido inicializada a cero) La instruccin +1008 lee el siguiente nmero, colocndolo en la localidad
08. La instruccin load, +2007, pone (copia) en el acumulador el primer nmero y la instruccin add,
+3008, suma el segundo nmero al que se encuentra en el acumulador. Todas las instrucciones aritmticas
de SML dejan sus resultados en el acumulador. La instruccin store, +2109, pone (copia) el resultado en
la localidad de memoria 09, de donde la toma la instruccin write, +1109, y lo imprime (como nmero
decimal de cuatro dgitos con signo) La instruccin halt, +4300, termina la ejecucin.
Ejemplo 1
Localidad
00
01
02
03
04
05
06
07
08
09

Nmero
+1007
+1008
+2007
+3008
+2109
+1109
+4300
+0000
+0000
+0000

Instruccin
(Lee A)
(Lee B)
(Carga A)
(Suma B)
(Almacena C)
(Escribe C)
(Terminacin)
(Variable A)
(Variable B)
(Resultado C)

El programa SML del ejemplo 2 lee del teclado dos nmeros y determina e imprime el valor ms alto. Note
que la instruccin +4107 se utiliza como transferencia de control condicional, a semejanza de la instruccin
if de C++.
Ejemplo 2
Localidad
00
01
02
03
04
05
06
07
08
09
10

APUNTADORES Y CADENA LECCIN 20

Nmero
+1009
+1010
+2009
+3110
+4107
+1109
+4300
+1110
+4300
+0000
+0000

Instruccin
(Lee A)
(Lee B)
(Carga A)
(Resta B)
(Bifurca con negativo a 07)
(Escribe A)
(Terminacin)
(Escribe B)
(Terminacin)
(Variable A)
(Variable B)

20-76

MIGUEL . TOLEDO MARTNEZ

Ahora escriba programas en SML que lleven a cabo las siguientes actividades.
a) Mediante un ciclo controlado por centinela lea 10 nmeros positivos y calcule e imprima su suma.
b) Mediante un ciclo controlado por contador, lea siete nmeros, algunos positivos y otros negativos, y
calcule e imprima su promedio.
c) Lea una serie de nmeros y determine e imprima el mayor. El primer nmero indicar la cantidad de
nmeros a procesar.
8.

(Simulador de computadora) Al principio parecer un tanto pretencioso, pero en este problema va a construir
su propia computadora. No, no se encargar de soldar sus componentes. En cambio, se valdr de la poderosa
tcnica de la simulacin basada en software para crear un modelo en software de la Simpletron. No s
decepcionar. Su simulador convertir en una Simpletron a la computadora en la que est trabajando y usted de
hecho podr ejecutar, probar y depurar los programas SML que escribi en el problema 18.
Cuando ejecute su simulador Simpletron, deber comenzar por imprimir:
*** Bienvenidos a Simpletron! ***
*** Por favor introduzca su programa una instruccin ***
*** (o dato) a la vez. Presentar ***
*** la localidad y un signo de interrogacin (?) . ***
*** Despus usted teclear la palabra para esa localidad. ***
*** Para detener la introduccin de su programa, ***
*** teclee el centinela -99999. ***
Simule la memoria de la Simpletron con el arreglo memoria de un solo ndice, que tiene 100 elementos.
Supongamos ahora que est ejecutndose el simulador y examinemos el dilogo a medida que ingresamos el
programa del ejemplo 2 del ejercicio 18:
00 ? +1009
01 ? +1010
02 ? +2009
03 ? +3110
04 ? +4107
05 ? +1109
06 ? +4300
07 ? +1110
08 ? +4300
09 ? +0000
10 ? +0000
11 ? -99999
12
*** Se ha completado la carga del programa ***
*** Inicia la ejecucin del programa
***
El programa SML ha sido colocado (o cargado) en el arreglo memoria. Ahora la Simpletron ejecuta su
programa SML. La ejecucin inicia con la instruccin de la localidad 00 y, como C++, contina
secuencialmente, a menos que se le dirija a otra parte del programa mediante una transferencia de control.
Mediante la variable acumulador represente el registro del acumulador. Emplee la variable contador para
llevar el registro de la localidad de memoria que contiene la instruccin en ejecucin. En la variable
codigoOperacion indique la operacin actual, es decir, los dos dgitos de la izquierda de la palabra de
instruccin. Mediante la variable operando indique la localidad de memoria sobre la que opera la
instruccin actual. As, operando est formado por los dos dgitos ms a la derecha de la instruccin que se
est ejecutando en el momento. No ejecute las instrucciones directamente en la memoria. En cambio,
transfiera la siguiente instruccin a ejecutar de la memoria a una variable llamada registroInstruccion.
Luego tome los dos dgitos de la izquierda, ponindolos en codigoOperacion, y tome los dos de la derecha
y colquelos en operando. Cuando comienza la operacin de la Simpletron, los registros especiales se
inicializan a 0.

APUNTADORES Y CADENA LECCIN 20

20-77

MIGUEL . TOLEDO MARTNEZ

Ahora hagamos el recorrido por la ejecucin de la primera instruccin SML, +1009. que est en la
localidad de memoria 00. A esto se le llama ciclo de ejecucin de instruccin.
contador nos dice la localidad de la siguiente instruccin a ejecutar. Obtenemos de la memoria el contenido
de dicha localidad mediante la instruccin de C++.
registroInstruccion = memoria[contador];
El cdigo de operacin y el operando se extraen del registro de instrucciones por medio de las
instrucciones:
codigoOperacion = registroInstruccion / 100;
operando = registroInstruccion %100;
Ahora la Simpletron deber determinar que el cdigo de operacin es en realidad una read, o lectura, (en
lugar de write, load, etc.) Una switch se encarga de diferenciar las doce operaciones de SML.
En la estructura switch se simula el comportamiento de varias instrucciones SML como sigue (dejaremos
las dems para que las resuelva el lector):
read:
load:
add:
branch:
halt:

cin >> memoria[ operando];


acumulador = memoria[operando];
acumulador += memoria[operando];
Pronto estudiaremos las instrucciones de bifurcacin
Esta instruccin imprime el mensaje
*** Ha terminado la ejecucin de Simpletron ***

y luego imprime el nombre y contenido de los registros, as como el contenido completo de la memoria. Tal
impresin con frecuencia es conocida como vaciado de computadora. Para ayudarle a programar su
funcin de vaciado, la figura 20.13 muestra un ejemplo de formato de vaciado. Observe que el vaciado tras
la ejecucin del programa Simpletron mostrara los valores reales de las instrucciones y de los datos al
momento de terminacin de la ejecucin.
Prosigamos con la ejecucin de la primera instruccin de nuestro programa: +1009 en la localidad 00.
Como hemos indicado, la instruccin switch simula esto ejecutando la instruccin C++:
cin >> memoria[ operando ];
Antes de la ejecucin de cin, se deber presentar un signo de interrogacin (?) en la pantalla, para pedirle al
usuario una entrada. La Simpletron espera a que el usuario introduzca un valor y oprima la tecla Retorno.
El valor queda en la localidad 09.
En este punto se ha completado la simulacin de la primera instruccin. Lo que queda es preparar a
Simpletron para que ejecute la siguiente instruccin. Dado que la instruccin que se acaba de ejecutar no
fue una transferencia de control, simplemente necesitamos incrementar el registro del contador de
instrucciones como sigue:
++contador;
Con esto se completa la ejecucin simulada de la primera instruccin. El proceso completo (es decir, el
ciclo de ejecucin de instruccin) inicia nuevamente con la obtencin de la siguiente instruccin a ejecutar.
Ahora consideremos la manera como se simulan las instrucciones de bifurcacin (las transferencias de
control) Todo lo que necesitamos hacer es ajustar el valor de contador de instrucciones. Por lo tanto, se
simula la instruccin de bifurcacin incondicional (40) dentro de switch con:
contador = operando;
La instruccin condicional bifurca si el acumulador es igual a cero se simula con:
APUNTADORES Y CADENA LECCIN 20

20-78

MIGUEL . TOLEDO MARTNEZ

if ( acumulador == 0)
contador = operando;
En este punto, usted deber implementar el simulador Simpletron y ejecutar los programo SML que
escribi en el problema 18. Puede embellecer el SML con caractersticas adicionales e incluirlas en su
simulador.
El simulador deber buscar varios tipos de errores. Durante la fase de carga del programa, por ejemplo,
cada nmero tecleado por el usuario para memoria deber estar en el rango de -9999 a +9999. El simulador
deber comprobar mediante un ciclo while que cada nmero digitado est en este rango y, si no, solicitar al
usuario que lo vuelva a teclear hasta que est correcto.
REGISTROS
acumulador
contador
registroInstruccin
codigoOperacion
operando

+0000
00
+0000
00
00

MEMORIA
0
10
20
30
40
50
60
70
80
90

0
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000

1
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000

2
3
4
5
6
+0000 +0000 +0000 +0000 +0000
+0000 +0000 +0000 +0000 +0000
+0000 +0000 +0000 +0000 +0000
+0000 +0000 +0000 +0000 +0000
+0000 +0000 +0000 +0000 +0000
+0000 +0000 +0000 +0000 +0000
+0000 +0000 +0000 +0000 +0000
+0000 +0000 +0000 +0000 +0000
+0000 +0000 +0000 +0000 +0000
+0000 +0000 +0000 +0000 +0000
Figura 20.13. Ejemplo de vaciado

7
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000

8
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000

9
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000
+0000

Durante la fase de ejecucin, el simulador deber buscar varios errores serios, como divisin entre cero,
cdigos de ejecucin invlidos, sobrecargas del acumulador (es decir, operaciones aritmticas que den
valores mayores que +9999 o menores que -9999) y otros. Tales errores serios se llaman errores fatales. Al
detectar un error fatal, el simulador deber imprimir un mensaje de error como:
*** Intento de dividir entre cero ***
*** Terminacin anormal de Simpletron ***
as como un vaciado completo con el formato que indicamos previamente. Con l se ayudar al usuario a
localizar el error en el programa.
MS PROBLEMAS DE APUNTADORES
9.

Modifique el programa de barajado y reparticin de naipes del programa BARAJAS.CPP, para que las
operaciones de barajado y reparticin se lleven a cabo mediante la misma funcin (barajarRepartir()) La
funcin deber contener una estructura de ciclo anidada semejante a la funcin barajar() del programa
BARAJAS.CPP.

APUNTADORES Y CADENA LECCIN 20

20-79

MIGUEL . TOLEDO MARTNEZ

10. Qu hace este programa?


// Problema21.cpp
#include <iostream.h>

//Para cout y cin

void misterio1(char *, const char *);


void main(void)
{
char cadena1[80], cadena2[80];
cout << Introduzca dos cadenas de caracteres: ;
cin >> cadena1 >> cadena2;
misterio1(cadena1, cadena2);
cout << cadena1 << endl;
}//Fin de main()
void misterio1(char *s1, const char *s2)
{
while(*s1 != \0)
++s1;
for(; *s1 = *s2; s1++, s2++)
;
//Instruccin vaca
}//Fin de misterio1()
11. Qu hace este programa?
//Problema11.cpp
#include <iostream.h>

//Para cout y cin

int misterio2(const char *);


void main(void)
{
char cadena[80];
cout << Introduzca una cadena de caracteres: ;
cin >> cadena;
cout << misterios2(cadena) << endl;
}//Fin de main()
int misterio2(const char *s)
{
for(int x = 0; *s != \0; s++)
++x;
return x;
en los siguientes segmentos de programa. Si es posible corregir el error, explique cmo.
12. Encuentre
}//Fineldeerror
misterio2()
13.
a)
b)
c)
d)
e)
f)
g)

int * numero;
cout << numero << endl;
float *realPtr;
long *enteroPtr;
enteroPtr = realPtr;
int *x, y;
x = y;

APUNTADORES Y CADENA LECCIN 20

20-80

MIGUEL . TOLEDO MARTNEZ

h) char s[] = ste es un arreglo de caracteres;


i) for(; *s != \0; s++)
cout << *s << ;
j) short *numPtr, resultado;
k) void *genericPtr = numPtr;
a. result = *genericPtr + 7;
l) float x = 19.34;
m) float xPtr = &x;
n) cout << xPtr << endl;
o) char *s;
p) cout << s << endl;
14. (Quicksort) En los ejemplos y ejercicios de la leccin 19 estudiamos las tcnicas de ordenamiento de burbuja,
ordenamiento en cubetas y ordenamiento por seleccin. Ahora presentamos la tcnica recursiva de
ordenamiento llamada Quicksort. El algoritmo bsico para un arreglo de un solo ndice es el siguiente:
a) Paso de particionamiento: tome el primer elemento del arreglo desordenado y determine su localidad
final en el arreglo ordenado, es decir, que todos los valores a la izquierda del elemento sean menores que
l y todos los valores a la derecha sean mayores. Ahora tenemos un elemento en su lugar correcto y dos
subarreglos desordenados.
b) Paso recursivo: Efecte el paso 1 sobre cada subarreglo desordenado.
Cada vez que se ejecuta el paso 1 sobre un subarreglo, se coloca otro elemento en su destino final en el
arreglo ordenado y se crean dos subarreglos desordenados. Cuando un subarreglo consiste de un elemento,
debe estar ordenado, por lo que dicho elemento est en su ubicacin final.
El algoritmo bsico parece bastante sencillo, pero cmo determinamos la posicin final del primer
elemento de cada subarreglo? Como ejemplo, considere el siguiente conjunto de valores (el elemento en
negritas es el elemento de particionamiento; se colocar en su posicin en el arreglo ordenado):
37
c)

89

10

12

68

45

Comenzando por el elemento ms a la derecha del arreglo, compare cada elemento contra 37 hasta que
encuentre uno menor que 37, luego intercambie 37 y dicho elemento. El primer elemento menor que 37 es
12, por lo que 37 y 12 se intercambian. El nuevo arreglo queda as:
12

89

10

37

68

45

El elemento 12 est en cursivas, indicando que se acaba de intercambiar con 37.


d) Comenzando por la izquierda del arreglo, pero iniciando con el elemento que sigue a 12, compare cada
elemento contra 37 hasta que se encuentre uno mayor que 37, luego intercambie 37 y dicho elemento. El
primer elemento mayor que 37 es 89, por lo que 37 y 89 se intercambian. El nuevo arreglo queda como:
12
e)

37

10

89

68

45

Comenzando por la derecha, pero iniciando con el elemento anterior a 89, compare los dems elementos
con 37 hasta encontrar uno menor que 37, luego intercambie 37 y dicho elemento. El primer elemento
menor que 37 es 10, por lo que 37 y 10 se intercambian. El nuevo arreglo queda como:
12

f)

10

37

89

68

45

Comenzando por la izquierda, pero iniciando con el elemento que sigue a 10, compare los elementos
contra 37 hasta encontrar uno mayor que 37, luego intercambie 37 y dicho elemento. No hay ms
elementos mayores que 37, as que, cuando comparamos 37 contra l mismo, sabemos que est en su
destino final en el arreglo ordenado.

Una vez aplicada la particin al arreglo anterior, quedan dos arreglos desordenados, El subarreglo con valores
menores que 37 contiene 12, 2, 6, 4, 10 y 8. El subarreglo con valores mayores que 37 contiene 89, 68 y 45. El
ordenamiento contina con el particionamiento de ambos arreglos de la misma manera que el arreglo original.

APUNTADORES Y CADENA LECCIN 20

20-81

MIGUEL . TOLEDO MARTNEZ

Basndose en el anlisis anterior, escriba la funcin recursiva quicksort() que ordena un arreglo de enteros con un
solo ndice. La funcin deber recibir como argumentos un arreglo de enteros, un ndice inicial y un ndice final.
quicksort() deber llamar a la funcin particin() para que lleve a cabo el paso de particionamiento.
14. (Recorrido por un laberinto) El siguiente tramado de # y puntos ( . ) es un arreglo de doble ndice que
representa un laberinto.
#
#
.
#
#
#
#
#
#
#
#
#

#
.
.
#
.
#
.
#
.
#
.
#

#
.
#
#
.
#
.
.
.
#
.
#

#
.
.
.
.
#
#
#
.
#
.
#

#
#
#
#
.
.
.
.
.
#
.
#

#
.
.
.
#
#
#
#
.
#
.
#

#
.
#
.
#
.
.
.
.
.
.
#

#
.
#
.
#
#
#
#
.
#
#
#

#
.
#
.
.
.
.
.
.
#
.
#

#
.
#
#
#
#
#
#
#
#
.
#

#
.
.
.
.
.
.
.
.
.
.
#

#
#
#
#
.
#
#
#
#
#
#
#

En este arreglo de doble ndice, los # representan las paredes del laberinto y los puntos los cuadros de las
rutas posibles a travs del laberinto. Slo se pueden hacer movimientos a una localidad del arreglo que
contenga un punto.
Hay un algoritmo sencillo de recorrido del laberinto que garantiza encontrar la salida (suponiendo que la
hay) Si no la hay, usted llegar nuevamente al punto de inicio. Ponga su mano derecha sobre la pared que
est a su derecha y avance. Nunca quite su mano de la pared. Si el laberinto da la vuelta a la derecha, siga
por la pared de la derecha. Siempre y cuando no quite su mano de la pared, tarde o temprano llegar a la
salida del laberinto. Podra haber una ruta ms corta que la que ha tomado, pero est garantizado que saldr
del laberinto si sigue este algoritmo.
Escriba la funcin recursiva travesia() que lo guiar por el laberinto. La funcin deber recibir como
argumentos un arreglo de caracteres de 12 por 12 que represente el laberinto, as como el punto de inicio de
dicho laberinto. A medida que travesia() intente localizar la salida del laberinto, deber poner una x en cada
cuadro de la ruta. La funcin deber desplegar el laberinto despus de cada movida, de modo que el usuario
pueda ver cmo se resuelve el problema.
15. (Generacin aleatoria de laberintos) Escriba la funcin generadorLaberinto(), que toma como argumento un
arreglo de caracteres de 12 por 12 y genera aleatoriamente un laberinto. Dicha funcin deber proporcionar el
punto de inicio y el de terminacin del laberinto. Pruebe la funcin travesa() del problema 25 sobre varios
laberintos generados al azar.
16. (Laberintos de cualquier tamao) Generalice las funciones travesia() y generadorLaberinto() de los
problemas 14 y 15 para que procesen laberintos de cualquier anchura y altura.
17. (Arreglos de apuntadores a funciones) Rescriba el programa FUNARRE3.CPP, (de la leccin 19, pgina 50)
para que opere mediante una interfaz operada por men. El programa deber ofrecer las siguientes 5 opciones al
usuario, como sigue (deben aparecer en la pantalla):

Introduzca una opcin:


0. Imprimir un arreglo de calificaciones
1. Encontrar la mnima calificacin
2. Encontrar la mxima calificacin
3. Imprimir el promedio de cada estudiante
4. Finalizar el programa
Una restriccin al uso de arreglos de apuntadores a funciones es que todos los apuntadores deben ser del
mismo tipo. Los apuntadores deben ser a funciones del mismo tipo de devolucin y que reciban argumentos
APUNTADORES Y CADENA LECCIN 20

20-82

MIGUEL . TOLEDO MARTNEZ

del mismo tipo. Por esta razn, las funciones del programa FUNARRE3.CPP se deben modificar para que
todas devuelvan el mismo tipo y tomen los mismos parmetros. Modifique las funciones minimo() y
mximo() de modo que impriman el valor mnimo o mximo y no devuelvan nada. En la opcin 3,
modifique la funcin promedio() del programa FUNARRE3.CPP para que enve a la salida el promedio de
cada estudiante (no de uno especfico) La funcin promedio() no deber devolver nada y deber tomar los
mismos parmetros que mostrarArreglo(), minimo() y mximo(). Almacene los apuntadores a las cuatro
funciones en el arreglo procesarOpcion() y tome la seleccin del usuario como el ndice del arreglo para
llamar a cada funcin.
18. (Modificaciones al simulador Simpletron) En el problema 7 se escribi un simulador en software de una
computadora que ejecuta programas escritos en el SML (lenguaje de mquina Simpletron) En este problema
proponemos vanas modificaciones y mejoras al simulador Simpletron. Algunas de las siguientes
modificaciones y mejoras podran ser necesarias para ejecutar los programas generados por el compilador.
a) Aumente la memoria del simulador Simpletron de modo que contenga 1000 localidades de memoria,
permitiendo el manejo de programas ms extensos.
b) Haga que el simulador pueda efectuar clculos de mdulo. Esto requiere una nueva instruccin en el
lenguaje de mquina Simpletron.
c) Permita que el simulador realice clculos de exponenciacin. Esto requiere una nueva instruccin en el
lenguaje de mquina Simpletron.
d) Modifique el simulador para que emplee valores hexadecimales, en lugar de valores enteros, para
representar instrucciones en lenguaje de mquina Simpletron.
e) Modifique el simulador de modo que permita enviar a la salida un salto de lnea. Esto requiere una nueva
instruccin en el lenguaje de mquina Simpletron.
f) Modifique el simulador para que procese valores de punto flotante, adems de valores enteros.
g) Modifique el simulador a fin de que maneje entrada de cadenas. Sugerencia: es posible dividir cada
palabra Simpletron en dos grupos, conteniendo cada una un entero de dos dgitos. Cada entero de dos
dgitos representa el equivalente decimal ASCII de un carcter. Agregue una instruccin en lenguaje de
mquina que acepte la entrada de una cadena y la almacene a partir de cierta localidad de memoria de la
Simpletron. La primera mitad de la palabra en dicha localidad ser la cuenta del nmero de caracteres de
la cadena (es decir, la longitud de la cadena) Cada media palabra subsiguiente contiene un carcter
ASCII, expresado como dos dgitos decimales. La instruccin en lenguaje de mquina convierte cada
carcter en su equivalente ASCII y lo asigna a media palabra.
h) Modifique el simulador para que maneje salida de cadenas almacenadas en el formato de la parte (g)
Sugerencia: agregue una instruccin de lenguaje de mquina que imprima una cadena a partir de cierta
localidad de memoria de la Simpletron. La primera mitad de la palabra de dicha localidad es la cuenta del
nmero de caracteres de la cadena (es decir, la longitud de la cadena) Cada media palabra subsiguiente
contiene un carcter ASCII expresado como dos dgitos decimales. La instruccin de lenguaje de mquina
comprueba la longitud e imprime la cadena, traduciendo cada nmero de dos dgitos a su carcter
equivalente.
19. Qu hace este programa?
//Programa19.cpp
#include <iostream.h>

//Para cout y cin

int misterio3(const char *, const char *);


void main(void)
{
char cadena1[80], cadena2[80];
cout << Introduzca dos cadenas de caracteres: ;
cin >> cadena1 >> cadena2;
cout
<<El resultado es
<< misterio3(cadena1, cadena2) << endl;
}//Fin de main()
int misterio3(const char *s1, const char *s2)
APUNTADORES Y CADENA LECCIN 20

20-83

MIGUEL . TOLEDO MARTNEZ

{
for(; *s1 != \0 && *s2 != \0; s1++, s2++)
if(*s1 != *s2)
return 0;
return 1;
}//Fin de misterio3()
PROBLEMAS DE MANIPULACIN DE CADENAS
20. Escriba un programa que, mediante la funcin strcmp, compare dos cadenas introducidas por el usuario. El
programa deber indicar si la primera cadena es menor, igual o mayor que la segunda.
21. Escriba un programa que compare, mediante la funcin strncmp, dos cadenas introducidas por el usuario. El
programa deber aceptar como entrada el nmero de caracteres a comparar, adems de indicar si la primera
cadena es menor, igual o mayor que la segunda.
22. Escriba un programa que se valga de la generacin de nmeros aleatorios para crear oraciones. El programa
utilizar cuatro arreglos de apuntadores a char, llamados articulo, sustantivo, verbo y preposicion. Deber
crear una oracin seleccionando una palabra al azar de cada arreglo en el siguiente orden articulo, sustantivo,
verbo, preposicion, articulo y sustantivo. A medida que se selecciona cada palabra, se le concatenar con las
palabras previas en un arreglo lo bastante grande para guardar la oracin completa. Las palabras se separarn
por espacios. La impresin de la oracin generada comenzar con una letra mayscula y terminar con un
punto. El programa generar 20 oraciones.
Los arreglos se llenarn como sigue: el arreglo articulo deber contener los artculos e, un, y, algn
y, cualquier; el arreglo sustantivo deber contener los sustantivos nio, seor, perro, pueblo y
auto; el arreglo verbo deber contener los verbos manejo, salt, corri, camin y evit; el
arreglo preposicion deber contener las preposiciones a, de, sobre, bajo y en.
Una vez que el programa haya sido escrito y est operando, modifquelo para que genere una historia corta que
consista de varias oraciones. (Qu tal la posibilidad de un escritor de cuentos para la clase de literatura?)
23. (Quintillas) Una quintilla es una oracin en verso, a veces jocoso, de cinco lneas, en el que el primero y
segundo versos riman con el quinto y el tercero con el cuarto. Mediante tcnicas parecidas a las desarrolladas en
el problema 22, escriba un programa en C++ que produzca quintillas al azar. Refinar este programa para que
genere buenas quintillas es un problema complejo, pero el resultado vale el esfuerzo.
24. Escriba un programa que codifique frases en pig Latin. (En ingls, pig Latin es un tipo de lenguaje que se
utiliza con propsitos de juego.) Hay muchas variaciones en los mtodos con los que se forman frases en pig
Latin. Por simplicidad, utilice el siguiente algoritmo:
Para formar una frase en pin Latin, divida en palabras dicha frase mediante la funcin strtok. Para convertir
cada palabra a pig Latin, ponga la primera letra de la palabra al final de ella y agregue las letras ay. As, la
palabra salta se convierte en altasay, las se vuelve aslay y computadora queda corno
omputadoracay. Los espacios entre las palabras quedan igual. Suponga lo siguiente: la frase consiste en
palabras separadas por espacios, no hay signos de puntuacin y todas las palabras tienen dos o ms letras. La
funcin mostrarLatinPalabra() deber desplegar cada palabra. Sugerencia: cada vez que se encuentre un token
durante una llamada a strtok, pase el apuntador del token a la funcin mostrarLatinPalabra() e imprima la
palabra en pig Latin.
25. Escriba un programa que acepte como entrada una cadena con un nmero telefnico, en la forma (555)5555555. El programa deber utilizar la funcin strtok para obtener como token el cdigo de rea, los primeros tres
dgitos del nmero telefnico y los ltimos cuatro dgitos. Los siete dgitos del nmero telefnico deben
concatenarse para formar una sola cadena. El programa deber convertir la cadena con el cdigo de rea a int y
la cadena con el nmero telefnico a long. Se deben imprimir el cdigo de rea y el nmero telefnico.
26. Escriba un programa que acepte como entrada una lnea de texto, la divida en tokens por medio de la funcin
strtok y enve los tokens a la salida en orden inverso.
27. Utilice las funciones de comparacin de cadenas estudiadas y las tcnicas de ordenamiento de arreglos
desarrollados para escribir un programa que alfabetice una lista de cadenas. Ordene los nombres de 10 o 15
pueblos de su zona.

APUNTADORES Y CADENA LECCIN 20

20-84

MIGUEL . TOLEDO MARTNEZ

28. Escriba dos versiones de la funcin de copia de cadenas y de la funcin de concatenacin de cadenas de la
figura 20.9. La primera versin deber utilizar indizacin de arreglos, y la segunda apuntadores y aritmtica de
apuntadores.
29. Escriba dos versiones de cada funcin de comparacin de cadenas de la figura 20.9. La primera versin debe
emplear indizacin de arreglos, y la segunda apuntadores y aritmtica de apuntadores.
30. Escriba dos versiones de la funcin strlen de la figura 20.9. La primera versin debe valerse de indizacin de
arreglos, y la segunda de apuntadores y aritmtica de apuntadores.
SECCIN ESPECIAL: Manipulacin avanzado de cadenas
Los problemas anteriores estn ligados a las lecciones y se disearon para poner a prueba los conocimientos del
lector sobre los conceptos fundamentales de manipulacin de cadenas. Esta seccin incluye un conjunto de
problemas de manipulacin de cadenas intermedios y avanzados. El lector deber encontrar interesantes y amenos
estos problemas. La dificultad de los problemas vara considerablemente. Algunos requieren de una hora o dos para
su escritura e implementacin. Otros son tiles corno tareas de laboratorio que podran tardar dos o tres semanas de
estudio e implementacin. Varios son proyectos vastos que se extendern durante todo el curso.
31. (Anlisis de texto) La disponibilidad de computadoras con capacidad de manipulacin de cadenas ha dado
como resultado algunos enfoques bastante interesantes para el anlisis de las obras de los grandes autores. Se ha
puesto mucha atencin en si William Shakespeare en realidad existi. Algunos estudiosos creen que hay
bastante evidencia que indica que las obras maestras atribuidas a Shakespeare en realidad fueron escritas por
Christopher Marlowe u otros autores. Los investigadores se han valido de las computadoras para encontrar las
similitudes en los escritos de ambos autores. Este ejercicio examina tres mtodos de anlisis de texto mediante
computadoras.
a) Escriba un programa que lea del teclado varias lneas de texto e imprima una tabla que indique el nmero
de veces que sucede cada letra del abecedario en el texto. Por ejemplo, la frase:
Ser o no ser: sa es la cuestin
contiene dos a, ninguna b, una c, etctera.
b) Escriba un programa que lea varias lneas de texto e imprima una tabla que indique el nmero de palabras
de una letra, de dos letras, de tres letras, etc., que hay en el texto. Por ejemplo, la frase
Si es ms noble para la mente sufrir
Contiene

c)

Longitud de la palabra

Veces

1
2
3
4
5
6
7

0
3
1
1
2
1
0

Escriba un programa que lea varias lneas de texto e imprima una tabla que indique el nmero veces que
sucede cada palabra del texto. La primera versin del programa deber incluir las palabras de la tabla en el
mismo orden en que aparecen en el texto. Por ejemplo, las lneas:
Ser o no ser: sa es la cuestin
Si es ms noble para la mente sufrir

contienen la palabra ser dos veces, la palabra ms una vez, no una vez, etc. Despus deber
intentar una impresin ms interesante (y til) en la que las palabras aparezcan ordenadas alfabticamente.
32. (Procesamiento de texto) Una importante funcin de los sistemas de procesamiento de texto es la justificacin
tipogrfica, es decir, la alineacin de las palabras tanto con el margen izquierdo como con el derecho. Esto
APUNTADORES Y CADENA LECCIN 20

20-85

MIGUEL . TOLEDO MARTNEZ

genera un documento de aspecto profesional que da la apariencia de haber sido formado tipogrficamente, en
lugar de escrito en una mquina de escribir. La justificacin tipogrfica puede lograrse en los sistemas de
cmputo insertando caracteres en blanco entre las palabras, de modo que la palabra ms a la derecha quede
alineada con el margen derecho.
Escriba un programa que lea varias lneas de texto y lo imprima en formato justificado. Suponga que texto se
debe imprimir en papel de 8 pulgadas de ancho y que se dejan mrgenes de una pulgada tanto al lado
izquierdo como al derecho. Suponga que la computadora imprime 10 caracteres por pulgada horizontal. Por lo
tanto, dicho programa deber imprimir 6 pulgadas de texto o 65 caracteres por lnea.
33. (Impresin de fechas con varios formatos) En la correspondencia comercial, las fechas por lo general se
imprimen en varios formatos. Dos de los ms comunes son:
21/07/55 y julio 21, 1955
Escriba un programa que lea una fecha con el primer formato y la imprima con el segundo.
34. (Proteccin de cheques) Con frecuencia las computadoras se utilizan para ejecutar sistemas de impresin de
cheques, como sucede con las aplicaciones de nmina y cuentas por pagar. Circulan por all muchas historias
extraas sobre cheques de nmina emitidos (accidentalmente) que amparan cifras de ms de un milln. Los
sistemas de cheques imprimen cifras extraas debido a errores humanos y/o a fallas de la mquina. Los
diseadores de sistemas integran controles en sus sistemas para evitar la emisin de tales cheques.
Otro problema serio es que alguien altere intencionalmente la cifra de un cheque para cobrarlo
fraudulentamente. Para evitar la alteracin de una cifra, la mayora de los sistemas de impresin de cheques
utiliza una tcnica llamada proteccin de cheques.
Los cheques diseados para impresin en una computadora contienen una cantidad fija de espacios en los que se
puede imprimir una cifra. Suponga que un cheque contiene ocho espacios en blanco, donde la computadora
debe imprimir el importe de un cheque de nmina. Si la cifra es grande, entonces se llenarn los ocho espacios.
Por ejemplo:
1,230.60
----------12345678

(importe del cheque)


(posiciones)

Por otra parte, si la cifra es menor que $100.00, entonces quedaran en blanco varios de los espacios. Por
ejemplo,
99.87
-----------12345678
contiene tres espacios en blanco. Si un cheque se imprime con espacios en blanco, es fcil de alterar. Para evitar
esto, muchos sistemas de impresin de cheques protegen la cifra insertando asteriscos iniciales como sigue:
***99.87
----------12345678
Escriba un programa que acepte una cifra y luego la imprima en formato de proteccin de cheques, con
asteriscos iniciales, de ser necesario. Suponga que se dispone de nueve espacios para imprimir la cifra.
35. (Escritura del importe de un cheque con palabras) Prosiguiendo el estudio del ejemplo previo, reiteramos la
importancia de disear sistemas de impresin de cheques que eviten la alteracin de sus importes. Un mtodo
de seguridad comn requiere que la cifra sea escrita tanto con nmeros como con palabras. Inclusive si alguien
es capaz de alterar la cifra numrica, es muy difcil modificar la cifra en palabras.

APUNTADORES Y CADENA LECCIN 20

20-86

MIGUEL . TOLEDO MARTNEZ

Muchos sistemas computarizados de impresin de cheques no imprimen la cifra del cheque con palabras. Tal
vez la razn principal de esta omisin es que la mayora de los lenguajes de alto nivel empleados para las
aplicaciones comerciales no cuentan con caractersticas adecuadas de manipulacin de cadenas. Otra razn es
que la lgica de la escritura de las cifras con palabras es un tanto complicada.
Escriba un programa en C++ que acepte una cifra numrica y la imprima con palabras. Por ejemplo, la cifra
112.43 deber escribirse como:
CIENTO DOCE con 43/100
36. (Cdigo Morse) Tal vez el esquema de codificacin ms famoso de todos es el cdigo Morse, desarrollador por
Samuel Morse en 1832 para el sistema telegrfico. El cdigo Morse asigna una serie de puntos y rayas a cada
letra del abecedario, a cada dgito y a algunos caracteres especiales (punto, coma, dos puntos y punto y coma)
En los sistemas audibles, el punto representa un sonido corto y la raya un sonido largo., Otras representaciones
de puntos y rayas son las empleadas en los sistemas de orientacin por luz y los de sealizacin con
indicadores.
La separacin entre palabras se indica mediante un espacio o, sencillamente, por la ausencia de un punto o raya.
En los sistemas sonoros, se indica un espacio mediante un periodo corto en el que no se transmite ningn
sonido. En la figura 20.14 aparece la versin internacional del cdigo Morse.
Escriba un programa que lea una frase en espaol y la codifique en cdigo Morse. Tambin escriba un
programa que lea una frase en cdigo Morse y la convierta en su equivalente en ingls. Ponga un espacio entre
cada letra codificada en Morse y tres espacios entre cada palabra.
Carcter

Cdigo

Carcter

Cdigo

A
.T
B
-...
U
..C
-.-.
V
...D
-..
W
.-E
.
X
-..F
..-.
Y
-.-G
--.
Z
--..
H
....
I
..
Dgitos
J
.--1
.---K
-.2
..--L
.-..
3
...-M
-4
....N
-.
5
.....
O
--6
-....
P
.--.
7
--...
Q
--.8
---..
R
.-.
9
----.
S
...
0
----Figura 20.14. Las letras del abecedario como se expresan con el cdigo Morse internacional
37. (Programa de conversin entre Sistema mtrico y sistema ingls) Escriba un programa que apoye al usuario
en las conversiones entre el sistema mtrico y el ingls. El programa deber permitirle al usuario especificar
como cadenas los nombres de las unidades (es decir, centmetros, litros, gramos, etc., en el sistema mtrico y
pulgadas, cuartillos, libras, etc., en el sistema ingls) y deber responder a preguntas sencillas como:
Cuntas pulgadas hay en 2 metros?
Cuntos litros hay en 10 cuartillos?
El programa deber reconocer las conversiones invlidas. Por ejemplo, la pregunta:
Cuntos pies hay en 5 kilogramos
APUNTADORES Y CADENA LECCIN 20

20-87

MIGUEL . TOLEDO MARTNEZ

no tiene sentido, pues los pies, son unidades de longitud, mientras que los kilogramos, son unidades de peso.
INTERESANTE PROYECTO DE MANIPULACIN DE CADENAS
38. (Generador de crucigramas) Mucha gente ha resuelto crucigramas, pero pocos han intentado generar uno. La
generacin de crucigramas es difcil. Aqu se sugiere como un proyecto de manipulacin de cadenas de alta
complejidad y esfuerzo. Hay muchos asuntos que el programador debe resolver para lograr que funcione incluso
el programa generador de crucigramas ms sencillo. Por ejemplo, cmo se representa el tramado de un
crucigrama en la computadora? Deber utilizarse una serie de cadenas o arreglos de doble ndice? El
programador necesita una fuente de palabras (es decir, un diccionario computarizado) al que pueda hacer
referencia directa el programa. De qu forma deben almacenarse estas palabras para simplificar las
complejas manipulaciones requeridas por el programa? El lector realmente ambicioso desear generar la
parte de claves del crucigrama, donde se imprimen las pistas para determinar las palabras horizontales y
verticales. La simple impresin de una versin en blanco del crucigrama es un problema complejo.

APUNTADORES Y CADENA LECCIN 20

20-88

Anda mungkin juga menyukai