Componentes clave
Encabezado Clases Funciones
<cstring> size_t strlen(char *cad)
En esta solución se muestra cómo realizar una tarea simple, pero útil: revertir una cadena terminada en un carácter nulo. Aunque la inversión de una cadena es una operación fácil para el programador experimentado, es una fuente común de preguntas para el principiante. Por esta sola razón merece su inclusión en este libro. Sin embargo, hay otras varias razones para incluirla. En primer lugar, hay muchas maneras de invertir una cadena, y cada variación ilustra una técnica diferente para manejar una cadena terminada en un carácter nulo. En segundo lugar, el mecanismo básico usado para inver- tir una cadena puede adaptarse a otros tipos de manipulaciones de cadena. Por último, demuestra en términos muy prácticos cómo manejar cadenas terminadas en un carácter nulo suele depender de código práctico de muy bajo nivel. A menudo, este código puede ser muy efi ciente, pero requiere más trabajo que el uso de la clase string.
En la solución mostrada aquí se invierte la cadena. Esto signifi ca que se modifi ca la cadena original. Por lo general, esto es lo que se necesita. Sin embargo, en la sección Opciones se muestra una variación que crea una copia inversa de la cadena.
Paso a paso
Hay muchas maneras de afrontar la tarea de invertir una cadena. En esta solución se usa un méto- do simple pero efectivo que está basado en el intercambio de extremo a extremo de los caracteres correspondientes de la cadena. Se pone este código dentro de una función llamada invcad().
1. Cree una función llamada invcad() que tenga este prototipo: void invcad(char *cad)
La cadena que habrá de invertirse se pasa a cad.
2. Dentro de invcad(), cree un bucle for que controle las dos variables que se usarán para indizar la matriz que contiene la cadena. Inicialice la primera variable en cero y auméntela
cada vez que se recorra el bucle. Inicialice la segunda variable en el índice del último carác- ter de la cadena y disminúyalo con cada iteración. Este valor se obtiene al llamar a strlen(). 3. Con cada paso que se recorra el bucle, intercambie los caracteres en los dos índices. 4. Detenga el bucle cuando el primer índice sea igual o mayor que el segundo índice. En este
punto, se invertirá la cadena.
Análisis
Como la mayoría de los lectores sabe, cuando se usa un nombre de matriz por sí solo, sin un índice, representa un apuntador a la matriz. Por tanto, cuando pasa una matriz a una función, en realidad sólo está pasando un apuntador a esa matriz. Esto signifi ca que una función que recibirá una cadena terminada en un carácter nulo como argumento debe declarar que su parámetro es de tipo char *. Por eso, el parámetro cad de invcad() se declara como char *cad.
Aunque cad es un apuntador, puede indizarse como una matriz, empleando la sintaxis normal de indización de matriz. Para invertir el contenido de una cadena, cree un bucle for que controla dos variables, que sirven como índices en la cadena. Un índice empieza en cero e indiza a partir del principio de la cadena. El otro índice empieza en el último carácter de la cadena. Cada vez que recorra el bucle, se intercambian los caracteres que se encuentran en los índices especifi cados. Lue- go, el primer índice se aumenta y se reduce el segundo. Cuando los índices convergen (es decir, cuando el primer índice es igual o mayor que el segundo), la cadena se invierte. He aquí la manera de escribir este bucle:
int i, j; char t;
for(i = 0, j = strlen(cad)-1; i < j; ++i, --j) { t = cad[i];
cad[i] = cad[j]; cad[j] = t; }
Observe que el índice del último carácter de la cadena se obtiene al restar uno del valor de- vuelto por strlen(). Aquí se muestra su prototipo:
size_t strlen(const char *cad)
La función strlen() devuelve la longitud de una cadena terminada en un carácter nulo, que es el número de caracteres en la cadena. Sin embargo, no se cuenta el terminador de carácter nulo. Debido a que el indizado de la matriz en C++ empieza en cero, debe restarse 1 a este valor para obtener el índice del último carácter en la cadena.
Ejemplo
Uniendo las piezas, he aquí una manera de escribir la función invcad(): // Invierte una cadena en el lugar.
void invcad(char *cad) { int i, j;
char t;
t = cad[i]; cad[i] = cad[j]; cad[j] = t; }
}
En el siguiente programa se muestra invcad() en acción: // Invierte una cadena en el lugar.
#include <iostream> #include <cstring> using namespace std; void invcad(char *cad); int main() {
char cad[] = "abcdefghijklmnopqrstuvwxyz"; cout << "Cadena original: " << cad << endl; invcad(cad);
cout << "Cadena invertida: " << cad << endl; return 0;
}
// Invierte una cadena en el lugar. void invcad(char *cad) {
int i, j; char t;
for(i = 0, j = strlen(cad)-1; i < j; ++i, --j) { t = cad[i];
cad[i] = cad[j]; cad[j] = t; }
}
Aquí se muestra la salida:
Cadena original: abcdefghijklmnopqrstuvwxyz Cadena invertida: zyxwvutsrqponmlkjihgfedcba
Opciones
Aunque invertir una cadena terminada en un carácter nulo es una tarea simple, permite algunas variaciones interesantes. Por ejemplo, el método usado en la solución depende de la indización de la matriz, que tal vez es la manera más clara de implementar esta función. Sin embargo, quizás no sea la más efi ciente. Una opción consiste en usar apuntadores en lugar de indización de matriz. Dependiendo del compilador que está usando (y las optimizaciones activadas), las operaciones de apuntador pueden ser más rápidas que la indización de matriz. Además, muchos programadores simplemente prefi eren el uso de apuntadores en lugar de indización de matriz cuando se recorre en ciclo una matriz de manera estrictamente secuencial. Cualquiera que sea la razón, la versión
de apuntador es fácil de implementar. He aquí una manera de retrabajar invcad(), de modo que sustituye las operaciones de apuntador para indización de matriz:
// Invierte una cadena en el lugar. Use apuntadores en lugar de indización de matriz.
void invcad(char *cad) { char t;
char *inc_p = cad;
char *dec_p = &cad[strlen(cad)-1]; while(inc_p <= dec_p) { t = *inc_p; *inc_p++ = *dec_p; *dec_p-- = t; } }
Una de las maneras más interesantes de revertir una cadena emplea la recursión. He aquí una implementación:
// Invierte una cadena en el lugar al usar recursión. void invcad_r(char *cad) {
invcad_recursiva(cad, 0, strlen(cad)-1); }
// A esta función se le llama con un apuntador a la cadena que se // invertirá y los índices de principio y final de los caracteres // que se invertirán. Por tanto, su primera llamada pasa cero // para inicio y strlen(cad)-1 para final. La posición del // terminador del carácter nulo no cambia.
void invcad_recursiva(char *cad, int inicio, int final) { if(inicio < final)
invcad_recursiva(cad, inicio+1, final-1); else return; char t = cad[inicio]; cad[inicio] = cad[final]; cad[final] = t; }
Observe que invcad_r() llama a invcad_recursiva() para revertir la cadena. Esto permite que se lla- me a invcad_r() únicamente con un apuntador a la cadena. Observe cómo las llamadas recursivas invierten la cadena. Cuando inicio es menor que fi nal, se hace una llamada recursiva a invcad_re- cursiva(); y el índice inicial aumenta en uno y el índice fi nal disminuye en uno. Cuando estos dos índices se unen, se ejecuta la instrucción return. Esto causa que las llamadas recursivas empiecen a devolverse, mientras se intercambian los correspondientes caracteres. Como algo interesante, puede usarse la misma técnica general para invertir el contenido de cualquier tipo de matriz. Su uso en una cadena terminada en un carácter nulo es simplemente un caso especial.
La última opción presentada aquí funciona de manera diferente de los métodos anteriores, porque crea una copia de la cadena original que contiene el inverso de la cadena original. Por tan- to, deja ésta sin cambio. Esta técnica es útil cuando no debe modifi carse la cadena original.
// Hace una copia inversa de una cadena.
void invcadcopia(char *cadr, const char *cadorg) { cadr += strlen(cadorg);
*cadr-- = '\0';
while(*cadorg) *cadr-- = *cadorg++; }
A esta función se le pasa un apuntador a la cad original en cadorg y uno a la matriz char que recibirá la cadena invertida en cadr. Por supuesto, la matriz señalada por cadr debe ser lo sufi cien- temente grande para contener la cadena invertida más el terminador nulo. He aquí un ejemplo de cómo puede llamarse a revsrtcpy():
char cad[5] = "abcd"; char inv[r];
invcad_copia(rev, cad);
Después de la llamada, inv contendrá los caracteres dcba y cad quedará sin modifi cación.