Sei sulla pagina 1di 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

3 37 4
6

4. Llamado de funciones por referencia


4.1. Ejemplo 20.2

6
7

5. Empleo del calificador const con apuntadores


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

10
11

6. Ordenamiento de burbuja mediante llamada por referencia


6.1. Ejemplos 20.8, 20.9, 20.10

15
15

7. Expresiones de apuntadores y aritmtica de apuntadores 8. Relacin entre apuntadores y arreglos


8.1. Ejemplos 20.11, 20.12

18 21
22

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


10.1. Ejemplo 20.13

24 25
28

11. Apuntadores y funciones


11.1. Ejemplos 20.14, 20.15

29
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

33
33 34
36

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

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

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

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

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

APUNTADORES Y CADENA LECCIN 20

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 7 contadorPtr hace referencia indirecta a una variable cuyo valor es 7. contador hace referencia directa 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 y 5

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> void main(void) { int a; int *aPtr; a = 7; aPtr = &a; cout //a es un entero //aPtr es un apuntador a un entero //Para cout y cin

//aPtr se establece a la direccin de a

<< "La direccin de a es: " << &a << "\nEl valor de aPtr es: " << aPtr; << "\n\nEl valor de a es: " << a << "\nEl valor de *Ptr es: " << *aPtr; << "\n\nMostrando que * y & son inversos " << "entre s. \n&*aPtr = " << &*aPtr << "\n*&aPtr = " << *&aPtr << endl;

cout

cout

}//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> int cuboValor(int); 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() //Para cout y cin //Prototipo

//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> void cuboReferencia(int *); 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() //Para cout y cin //Prototipo

//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() n 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() int cuboValor(int n) { return n * n * n; }//Fin de cuboValor() n 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(): void cuboReferencia(int *nPtr) { nPtr *nPtr = * nPtr * *nPtr * *nPtr; indefinido }//Fin de cuboValor()

int main() { int numero = 5;

numero 5

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> 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() //Para cout y cin //Para toupper()

//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> void f(const int *); void main(void) { int y; f(&y); }//Fin de main() //f intenta una modificacin ilegal //Para cout y cin

APUNTADORES Y CADENA LECCIN 20

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> 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 //Para cout y cin

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> 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

//Para cout y cin

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> 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; //Para cout y cin

}//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> //Para cout y cin //Para setw()

APUNTADORES Y CADENA LECCIN 20

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 v[0]

3004 v[1]

3008 v[2]

3012 v[3]

3016 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 v[0] 3004 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> 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"; //Para cout y cin

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] palos[0] palos[0] palos[0]

C D T E

o i r s

a
m

z a o
d

o
n

e e s \0

s s \0

\0 \0

a e
p

t e s

l a

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 Corazones 0 Diamantes 1 Trboles 2 Espadas 3 1 2 3 4 5 6 7 8 9 10 11 12

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] = {2, 6, 4, 8, 10, 12, 89, 68, 45, 37};

APUNTADORES Y CADENA LECCIN 20

20-29

MIGUEL . TOLEDO MARTNEZ

<< "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() = *elemento1Ptr; = *elemento2Ptr; = temporal;

cout

//Intercambia si b es menor que a

//Intercambia si b es mayor que a

APUNTADORES Y CADENA LECCIN 20

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> 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 //Para cout y cin

<< "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
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.

Descripcin de la funcin

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> 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'; //Para cout y cin //Para strcpy() y strncpy()

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> //Para cout y cin //Para strcat() y strncat()

APUNTADORES Y CADENA LECCIN 20

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> 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); << "\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; //Para cout y cin //Para setw() //Para strcmp() y strncmp()

cout

}//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> //Para cout y cin //Para strtok()

APUNTADORES Y CADENA LECCIN 20

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> 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; //Para cout y cin //Para strlen()

}//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> 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() //Para cout y cin

/* 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> 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() //Para cout y cin

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> void main(void) { char cadena[] = "\"!Alto!\", dijo l."; cout << cadena; }//Fin de main() //Para cout y cin

/* 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> void main (void) { char cadena[256]; int i; // Cadena introducida por el usuario // ndice a utilizar en la cadena //Para cout y cin

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> //Para cout y cin //Para strlen()

APUNTADORES Y CADENA LECCIN 20

20-41

MIGUEL . TOLEDO MARTNEZ

void main(void) { char tituloLibro[] = "\"El Coronel no tiene quien le escriba\""; cout }//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() **************************************************************************** */ << tituloLibro << " contiene " << strlen(tituloLibro) << " caracteres";

/* 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> 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

//Para cout y cin //Para strcpy()

20-42

MIGUEL . TOLEDO MARTNEZ

#include <iostream.h> #include <string.h> void main(void) {

//Para cout y cin //Para strcat()

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> 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++)) ; //Para cout y cin //Para strncat()

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> 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() //Para cout y cin //Para strxfrm()

/* 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> 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() ************************************************************************** */ //Para cout y cin //Para strlwr() y strupr()

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> 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() //Para cout y cin //Para strchar()

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> 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() //Para cout y cin //Para strcmp()

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> void main(void) { cout cout cout cout }//Fin de main() << "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); //Para cout y cin //Para stricmp() y strncmpi()

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> void main(void) { int intResultado; float floatResultado; long longResultado; intResultado = atoi("1234"); floatResultado = atof("12345.678"); longResultado = atol("1234567L"); cout }//Fin de main() << intResultado << ' ' << setprecision(8) << floatResultado << ' ' << longResultado; //Para cout y cin //Para las funciones anteriores //Para setprecision()

/* 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> 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() //Para cout y cin //Para strdup()

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> void main(void) { cout << "Buscando la subcadena Abc en la cadena AbcDef: " << strspn("AbcDef", "Abc") << endl; << "Buscando la subcadena cbA en la cadena AbcDef: " << strspn("AbcDef", "cbA") << endl; << "Buscando la subcadena Def en AbcAbc: " << strspn("AbcAbc", "Def"); //Para cout y cin //Para strspn()

cout

cout }//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> void main(void) { cout << "Buscando la subcadena Abc en la cadena AbcDef: " << (strstr("AbcDef", "Abc") ? "Encontrado" : "No encontrado") << endl; << "Buscando la subcadena Abc en la cadena abcDef: " << (strstr("abcDef", "Abc") ? "Encontrado" : "No encontrado") << endl; << "Buscando la subcadena Abc en la cadena AbcAbc: " << (strstr("AbcAbc", "Abc") ? "Encontrado" : "No encontrado"); //Para cout y cin //Para strstr()

cout

cout

}//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> 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() //Para putchar //Para _toupper() y toupper()

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> 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() //Para putchar() //Para tolower() y _tolower()

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> 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> 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; //Para cout y cin //Para strlen(), strcat() //Para sscanf() y printf()

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. 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.

2.

3. 4. 5. 6.

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. 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.

3. 4. 5.

APUNTADORES Y CADENA LECCIN 20

20-66

MIGUEL . TOLEDO MARTNEZ

PROPUESTAS DE DESEMPEO
1. 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.

2. 3.

PROPUESTAS DE PORTABILIDAD
1. 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 ).

2. 3.

4.

5.

6. 7.

OBSERVACIONES DE INGENIERA DE SOFTWARE


1. 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.

2. 3.

4. 5.

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. 20-70

APUNTADORES Y CADENA LECCIN 20

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. 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) 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? 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.

3.

4.

5.

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 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

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

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; const int WRITE = 11; Lee a palabra del teclado y la pone en una localidad especfica de la memoria
Escribe en la pantalla la palabra ubicada en una localidad especfica de memoria.

Operaciones de carga/almacenamiento: const int LOAD = 20; Const int STORE = 21; Carga en el acumulador la palabra ubicada en una localidad especfica de memoria. 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). 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). 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). Multiplica la palabra ubicada en una localidad especfica de memoria por la palabra que se encuentra 20-75

const int SUBTRACT = 31;

const int DIVIDE = 32;

const int MULTIPLY = 33;


APUNTADORES Y CADENA LECCIN 20

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; const int BRANCHNEG = 41; const int BRANCHZERO = 41; const int HALT = 43; Bifurca a una localidad especfica de memoria. Bifurca a una localidad especfica de memoria si el acumulador es negativo. Bifurca a una localidad especfica de memoria si el acumulador es igual a cero. 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

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)

APUNTADORES Y CADENA LECCIN 20

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 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

+0000 00 +0000 00 00

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; error en los siguientes segmentos de programa. Si es posible corregir el error, explique cmo. 12. Encuentre }//Finel de 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; 20-80

APUNTADORES Y CADENA LECCIN 20

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) 2 6 4 89 8 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 2 6 4 89 8 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) 2 6 4 37 8 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 2 6 4 10 8 37 89 68 45

f)

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 Longitud de la palabra 1 2 3 4 5 6 7 c) Veces 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

Potrebbero piacerti anche