UTNianos

Versión completa: Ejercicio 4 del tp de strings (ayuda)
Actualmente estas viendo una versión simplificada de nuestro contenido. Ver la versión completa con el formato correcto.
Hola gente, como va?

Bueno aca estoy teniendo un problema con el ejercicio 4 de la guia de strings que dice:

"Escribir una función que reciba como argumento puntero a char, la asuma como
una secuencia en ASCII terminada en '\0' (NULL) y devuelva la secuencia
invertida.

void string_reverse (char *)"


Yo lo hice asi:

http://www.copiatelo.com/index.php?show=m19bc71d6

Pero evidentemente no funciona. Creo que estoy fallando a la hora de querer volver el puntero a la primera posicion de la cadena donde almaceno mi palabra (cadena "palabra") en la linea 36.

Muchas gracias, saludos!
(01-09-2013 18:06)Gonsha escribió: [ -> ]Hola gente, como va?

Bueno aca estoy teniendo un problema con el ejercicio 4 de la guia de strings que dice:

"Escribir una función que reciba como argumento puntero a char, la asuma como
una secuencia en ASCII terminada en '\0' (NULL) y devuelva la secuencia
invertida.

void string_reverse (char *)"


Yo lo hice asi:

http://www.copiatelo.com/index.php?show=m19bc71d6

Pero evidentemente no funciona. Creo que estoy fallando a la hora de querer volver el puntero a la primera posicion de la cadena donde almaceno mi palabra (cadena "palabra") en la linea 36.

Muchas gracias, saludos!

El tema es que estás perdiendo la posición.

Al avanzar con el puntero original estás perdiendo la primera posición y luego estás asignando, o queriendo asignar la primer pos de algo que ya perdió.
Creo que había un par de estrategias distintas para solucionar esto, podrías usar un puntero auxiliar para recorrer tu string y así contar la cantidad de posiciones.
Puede ser, no se. De todas formas corregi el programa y quite el algoritmo de contar la cantidad de palabras, y lo reemplaze por la funcion "strlen" (el enunciado en ningun momento dice que no se puede utilizar). Pero aun asi, el programa no funciona.
(01-09-2013 18:50)Gonsha escribió: [ -> ]Puede ser, no se. De todas formas corregi el programa y quite el algoritmo de contar la cantidad de palabras, y lo reemplaze por la funcion "strlen" (el enunciado en ningun momento dice que no se puede utilizar). Pero aun asi, el programa no funciona.

Ahora sí, con la cuestión esa no presté atención a como estabas asignando los caracteres al vector:

Ponelo así
//ojo con la posición cero del vector (lo pongo explicito)
cant_posiciones = cant_posiciones-1

while (*palabra != NULL) {
auxiliar [cant_posiciones - posicion] = *palabra;
palabra ++;
posicion ++;
}

Le asignás el contenido del puntero. Se pueden manejar los punteros como arrays pero entonces tendrías que fijarte lo que le querés mandar es un char solo.

Slds
fijate que la funcion "ejercicio 4" no devuelve nada.

""Escribir una función que reciba como argumento puntero a char, la asuma como una secuencia en ASCII terminada en '\0' (NULL) y devuelva la secuencia invertida."


usa strlen.
una vez que obtenes el largo del string tenes dos variables, el contador "i" y "largodelstring".
como hizo gonza87
Hora me funciona "Bien", pero no se como meter el null al final. Creo que por eso es que cuando me invierte la cadena, al comienzo me pone un caracter raro. Tenes idea que puede ser?

(01-09-2013 19:21)Maik escribió: [ -> ]fijate que la funcion "ejercicio 4" no devuelve nada.

""Escribir una función que reciba como argumento puntero a char, la asuma como una secuencia en ASCII terminada en '\0' (NULL) y devuelva la secuencia invertida."


usa strlen.
una vez que obtenes el largo del string tenes dos variables, el contador "i" y "largodelstring".
como hizo gonza87

Como haria para devolver la cadena?
ni me acuerdo xD

podes devolver el puntero.

acordate de agregar el /null al final.
(01-09-2013 19:33)Maik escribió: [ -> ]ni me acuerdo xD

podes devolver el puntero.

acordate de agregar el /null al final.

Y se pero no se como agregarlo, por eso lo pregunte antes xd. Deberia devolver el puntero a la primera posicion de mi cadena "auxiliar" pero no se como hacerlo xd.
seria algo asi como "return (*string)"

y cuando llamas a la funcion

stringdadovuelta[0] = ejercicio4 (*cadenadato)


pero ni me acuerdo. google->!
Maik en la guia de TP's dicen que el prototipo de la func debe ser:

void string_reverse (char *)

Asique no devuelve nada. Esta mal redactado =P.
No es que no devuelve nada, la idea del proceso que tenes que hacer es esta:

suponete que a la variable "cad" le asignas "hola mundo"
cuando ejecutas el proceso string_reverse (cad)
el valor de cad deberia quedar en "odnum aloh"

No esta mal redactado
Hola, vengo a contribuir con una solucion *carlitox*



#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define N 50

void invertir_cadena(char *cadena)
{
int i, longitud = strlen(cadena) - 1;
char *cadena_auxiliar = strdup(cadena);

//hasta que no cubramos la longitud, copiamos del final de la cadena auxiliar en la cadena
for(i = 0; i <= longitud; i++)
cadena[i] = cadena_auxiliar[longitud-i];

free(cadena_auxiliar);
}

int main(void)
{
char cadena[N] = "devolveme invertida, guachin!";

printf("\n La cadena: \"%s\" \n", cadena); //mostramos la cadena

invertir_cadena(cadena); //llamamos a la función del ejercicio

printf("\n Invertida es: \"%s\" \n\n", cadena); //la mostramos invertida, magia

return EXIT_SUCCESS;
}



En una de esas si lo mirás un toque te podes ahorrar alguna línea mas.

EDIT: En tu codigo el problema es que cuando terminas de contar las posiciones, como el operador ++ tiene efecto de lado, tu palabra queda sobre el \0 final
osea, nunca estas entrando al while que copia en la auxiliar, y te queda que al final la auxiliar tambien es solamente \0 (no muestra nada)
lo primero que se me ocurre para resolverlo es guardarte la direccion de memoria de la palabra antes de contar las posiciones y despues reestablecerlo
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 =P


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!

[Imagen: internet-meme1365367926-291x375.jpg]

"¿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...
el enunciado dice que el prototipo es: void string_reverse(char *); entre la ambiguedad de la palabra "devuelve" y el prototipo me quedo con el prototipo

no creo que los profes prohiban el uso de las funciones del standard, no seria muy inteligente, para que vamos a enseñarle a los pibes a no reutilizar codigo?
(01-09-2013 22:09)Desert69 escribió: [ -> ]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 =P


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!

[Imagen: internet-meme1365367926-291x375.jpg]

"¿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...
Gracias desert, este era otro ejercicio, ya lo había hecho funcionar igual =). Lo de malloc me parece inútil igual, osea no tiene sentido acomplejar un problema tan sencillo.

Ahora volvete al otro thread el de listas y armate una respuesta similar porfa =P.
URLs de referencia