Gon, todavía no leí tu código, sólo leí "por arriba" lo que hablaron en este thread. BTW, ¿es el mismo ejercicio que me habías mandado antes o es distinto? Así busco el otro si hiciera falta
Primero pensemos qué tenemos que hacer, después cómo lo haríamos, y después tratemos de escribir ese cómo. Lo interesante de esto es ir avanzando de a poco en los constraints de la solución, cosa de empezar "en un mundo ideal" para que la propuesta de solución sea lo más "pura" posible, y de a poco ir afeándola (siempre lo menos posible) hasta terminar en algo compilable y ejecutable. Si arrancamos pensando en las restricciones del lenguaje y en detalles, vamos como "perdiendo el foco".
--------------------------------
El enunciado pide definir string_reverse(), que recibe un string (osea, una lista de chars terminada por un caracter especial - \0), y que debe devolver ese mismo string pero dado vuelta. Algunos posibles casos de ejemplo (test cases, que le llaman) sería hacer que "Hola, mundo!" se convierta en "!odnum ,aloH", "meh" se convierta en "hem", "a" se convierta en "a" y "" se convierta en "" (el string vacío).
Si nuestro string es una lista de caracteres, eso significa que tienen un orden. Digamos, "hola" no es lo mismo que "olah": está compuesta por los mismos caracteres, pero el orden es distinto. Entonces, si hay un orden, podríamos decir que cada caracter tiene un número de orden (lo que se llama "índice" o "index" en inglés). Como sabemos que estamos en C y son todos hippies locos, vamos a empezar indexando por el 0: en "hola", la "h" ocupa la posición 0, la primer "o" está en la posición 1, y la a está en la posición 3. Mirando un poco lo que deseamos conseguir ("aloh"), vemos que la posición 0 la ocupa la "a", que antes ocupaba la posición 3 (la última), mientras que la "h" ahora está en la posición 3 (última), siendo que antes era la primera. Siguiendo un poco más, si nuestro string fuera "warhola", al darla vuelta la última "a" volvería a estar en la primer posición, y la "w" pasa a la última, que en este caso es la 6. ¿Qué cambió entre una y otra palabra para que la primer letra del string original pase a estar en otra posición del resultado? Claro que sí: cambió la longitud de la palabra.
Entonces, analizando todos estos strings, encontramos más o menos una regla "invariante": cada caracter pasa a estar en la posición longitudDeLaPalabra - posicionEnLaOriginal - 1 (el menos 1 es porque pasamos de ordinales a cardinales, o como carajo se llamaran los otros). Así, en "hola"->"aloh", longitudDeLaPalabra es 4, y, por ejemplo, para la "h" tenemos posicionEnLaOriginal 0 => posición final es 3 (4 - 0 - 1). Y funciona para todas las letras. Fiesta.
¿Y con la "a"? Bueno, la longitud es 1, y la posicion original es 0: 1 - 0 - 1 = 0 => la "a" va en la posición 0. ¿Tiene sentido, no?
¿Y si es ""? Buen, je, ahí nos cabe un poco. Con longitud 0, 0 - 0 - 1 daría -1, puaj. Peeeeero, podemos usar como caso "extraño" esto de que la longitud es 0, y hacer que para esos strings no hagamos nada (vamos, el único string con longitud 0 es el vacío, y si está vacío no tenemos nada para hacer).
Volviendo a alto nivel, tenemos que devolver un nuevo string. Como el tamaño es variable, ese string lo vamos a reservar en memoria dinámica (además, necesitamos que siga siendo visible después de que finalize nuestra función).
Entonces nuestra función va a reservar un cacho'e memoria, y escribir ahí uno por uno los caracteres que vienen de la otra, leyendolos según la regla loca esta que inventamos.
Y sólo nos queda un detalle: los \0. TODOS los strings tienen que terminar en \0. En C el "hola" es en realidad un "hola\0". Y ese \0 no queremos darlo vuelta: "hola\0" pasa a ser "aloh\0", y no "\0aloh". Entonces, cuando nos llegue un string, su longitud no va a ser contando incluso el byte del \0 para nuestra fórmula loca, pero sí vamos a tener que tenerlo en cuenta para reservar su memoria (si para "hola\0" sólo reservamos 4 bytes, cuando querramos escribir el \0 del final vamos a hacer cagadas, y no tener un \0 al final va a ser incluso peor, porque nuestro resultado no va a ser un string - será sólo un stream, y cualquier función que espere un string va a reventar, potencialmente).
Ya definimos todo. Emepecemos a escribir C.
Lo primero, el prototipo de nuestra función: recibe un string, y devuelve otro.
char *string_reverse(char *source) {
// TODO: implementar
}
Ahora, necesitaríamos reservar el lugar en que vamos a escribir. Para poder hacerlo, primero tenemos que saber cuántos bytes necesitamos reservar. Consigamos la longitud del string:
#include <string.h>
char *string_reverse(char *source) {
size_t text_length = strlen(source);
// TODO: implementar
}
strlen() da la longitud del string, por lo que no cuenta el \0. Es lo que definimos antes como longitudDeLaPalabra (que no es una única palabra en realidad, ja). Ahora sí, reservemos el lugar:
#include <string.h>
#include <stdlib.h>
char *string_reverse(char *source) {
size_t text_length = strlen(source);
char *reversed = malloc(text_length + 1);
// TODO: implementar
}
Pedimos un byte más que la longitud del texto porque necesitamos el \0 como terminador (string vs stream, bleh).
Y, ahora, empecemos a escribir. Tendriamos que ir caracter por caracter, copiando de a uno según la fórmula que dimos. Como sabemos hasta qué longitud contar, usemos un for para ir variando un índice nuestro:
#include <string.h>
#include <stdlib.h>
char *string_reverse(char *source) {
size_t text_length = strlen(source);
char *reversed = malloc(text_length + 1);
for(int target_index = 0; target_index < text_length; target_index++) {
// TODO: implementar
}
// TODO: implementar
}
Ese for dice "por cada índice entre 0 y text_length". ¿Qué tenemos que hacer por cada uno de esos? ¡Copiar el caracter correspondiente!
#include <string.h>
#include <stdlib.h>
char *string_reverse(char *source) {
size_t text_length = strlen(source);
char *reversed = malloc(text_length + 1);
for(int target_index = 0; target_index < text_length; target_index++) {
reversed[target_index] = source[text_length - target_index - 1];
}
// TODO: implementar
}
En cada posición, metemos el caracter que estaba en la posición que calculamos con la fórmula.
¿Y ahora qué nos falta? ¡El \0, claro! Y ya que estamos, devolvemos nuestro string (hacer esto en dos pasos ya daba paja).
#include <string.h>
#include <stdlib.h>
char *string_reverse(char *source) {
size_t text_length = strlen(source);
char *reversed = malloc(text_length + 1);
for(int target_index = 0; target_index < text_length; target_index++) {
reversed[target_index] = source[text_length - target_index - 1];
}
reversed[text_length] = \0;
return reversed;
}
¡Fiesta! ¡Terminamos!
"¿Y eso del string vacío?"
Bueno, medio que viene gratis el asunto. Pensemos que con el string vacío, text_length va a valer 0. En ese caso, el malloc va a de ser de un byte, y la condición del for va a fallar de entrada (target_index 0 no es menor que text_length 0). Entonces, nunca se ejecuta la instrucción dentro del for, y luego en reversed[0] se pone un \0 y se devuelve. Es tal cual lo que queríamos.
"Che, pero, si recibo un string vacío, por qué no devolver el mismo source y ahorrarme un byte de memoria?"
Bueno, sí, podría interesarnos eso, pero hay un problema: el contrato de nuestra función dice que devuelve un nuevo string, porque otra no nos queda (tenemos que reservar nosotros el espacio para ese string, porque sería feo que nos lo manden desde afuera). Entonces, eso significa que quien la use ya sabe que estamos haciendo un malloc(), y que va a ser él el responsable de hacer el free() correspondiente en algún momento (porque nosotros perdemos el control después de devolverlo). Entonces, si para el caso del string vacío devolvieramos el string source, cuando le quieran hacer free() estamos dando lugar a que se rompa todo (porque source era una constante, o porque ya le estaban haciendo el free() a source y terminan haciendo un doble free()). Por eso, si el contrato dice que devolvemos un nuevo string, ****siempre**** devolvamos un nuevo string.
--------------
That's it
Cualquier cosa, chifle nomás.
PD: viendo la solución carlitox, usar strdup() es un pequeño hackcito para resolver lo del strlen() + malloc() (igual, no crean que va a hacer algo distinto a eso la función, je
). Por otro lado, tanto el enunciado que escribiste como la solución de carlitox dicen que la función devuelve void. En ese caso, se contradice con lo de que "devuelve el string cambiado". Más bien sería "cambia el string parámetro", no devuelve nada.
Si ese fuera el caso, la solución esa está bastante bien...