Foros del Web » Programación para mayores de 30 ;) » C/C++ »

[SOLUCIONADO] Problema con array, char y int

Estas en el tema de Problema con array, char y int en el foro de C/C++ en Foros del Web. Que tal amigos. Veréis, estoy intentando guardar nombres de personas en un array pero todo el rato me estoy encontrando con el siguiente problema. Mirad, ...
  #1 (permalink)  
Antiguo 21/11/2013, 07:50
 
Fecha de Ingreso: marzo-2010
Mensajes: 191
Antigüedad: 14 años, 1 mes
Puntos: 3
Pregunta Problema con array, char y int

Que tal amigos.

Veréis, estoy intentando guardar nombres de personas en un array pero todo el rato me estoy encontrando con el siguiente problema.

Mirad, os pongo el código y hablamos:

Código C:
Ver original
  1. //declaro la array
  2. int x;
  3. char array[10];
  4.  
  5. //con esto rescato la última palabra de una frase de dos palabras
  6. char *nombre;
  7. nombre = strtok(frase, " ");
  8. nombre = strtok(NULL, " ");
  9.  
  10. //guardo la palabra en el array
  11. array[0]=*nombre;
  12.  
  13. //muestro el nombre por pantalla
  14. printf( "%s", array[0] );

Pues a la hora de mostrar el nombre por pantalla (es decir, en el último paso) me peta, no hay manera...

No sé que estoy haciendo mal, con lo que estaría agradecido a ver si alguien localiza el error :/

Muchas gracias, como siempre.

Saludos!!
  #2 (permalink)  
Antiguo 21/11/2013, 13:17
 
Fecha de Ingreso: agosto-2012
Mensajes: 601
Antigüedad: 11 años, 8 meses
Puntos: 83
Respuesta: Problema con array, char y int

Tienes que revisar los tipos de datos y lo que soportan. En la linea siguiente

Código C:
Ver original
  1. array[0]=*nombre;

estas asignando la direccion de memoria (un puntero de 4 bytes, todos los punteros ocupan 4 bytes) a un caracter de 1 byte, es decir que intentas meter 4 cosas donde solo caben 1.

Los arrays de texto no son tal como parecen, es decir que el array[10] no estas reservando memoria para 10 palabras, sino que estas reservando memoria para 1 sola palabra de 10 caracteres como maximo contando el \0 final.

Hay varias formas de solucionarlo. Sigo con tu ejemplo:

Código C:
Ver original
  1. char *nombre;
  2. nombre = strtok(frase, " ");
  3. nombre = strtok(NULL, " ");

Mientras esté activo el ambito de la variable 'frase', el puntero 'nombre' será valido (por cierto, te comento que strtok modifica la variable de entrada); eso significa que si quieres trabajar con ese puntero debes hacerlo dentro de ese ambito.

La otra forma es copiar el texto a otro texto; esto implica que el otro texto sea como minimo de la misma longitud+1 que el texto que le vas a copiar. Por ejemplo:

Código C:
Ver original
  1. char frase[] = "qwe asdfg";
  2. char *nombre;
  3.  
  4. nombre = strtok(frase, " ");
  5. nombre = strtok(NULL, " ");

Ahora en 'nombre' tienes el texto 'asdfg' que ocupa 5 bytes, pero ahora puedes ver la diferencia com lo que hacias antes:

Código C:
Ver original
  1. nombre //esto es el puntero al texto
  2. *nombre//esto es la direccion de memoria al puntero

Para copiar el texto del puntero a otro texto sin asignar la direccion de memoria debes disponer de una variable de (en este caso) 6 bytes (5 de los caracteres + 1 del \0):

Código C:
Ver original
  1. char copia[6];
  2. sprintf(copia, nombre);

Con esto tienes una copia de una sola palabra. Si quieres hacer una lista de palabras hay varias formas, la mas simple es usar una estructura que contenga un buffer de texto de longitud estandar e implementar una lista simple. La otra forma es mas compleja y consiste en crear una lista de texto separada por nulos.

Dependiendo de lo que quieras hacer puedes usar una u otra. De momento investiga un poco de que van las estructuras y como crear memoria dinamica para crear una lista. Todo esto suponiendo que estas en C, si estas en c++ tienes el objeto string y el contenedor vector, con lo que facilmente construyes un array de strings.

Saludos
vosk
  #3 (permalink)  
Antiguo 21/11/2013, 15:42
 
Fecha de Ingreso: marzo-2010
Mensajes: 191
Antigüedad: 14 años, 1 mes
Puntos: 3
Respuesta: Problema con array, char y int

Que tal Vosk!

Pues investigando un poco más he utilizado el siguiente método

Código C:
Ver original
  1. //creo un array de char de 10 caracteres
  2. char array[10];
  3.  
  4. //hago lo que tengo que hacer, los strtoks y demás
  5. [...]
  6.  
  7. //guardo el nombre al array
  8. strcpy(array,nombre);
  9.  
  10. //muestro el nombre por pantalla
  11. printf( "%s", array );

Y si, me lo muestra.

Lo que pasa es eso, continuo ejecutando la aplicación (con el típico menú do...while) y por mucho que introduzca usuarios, el array no sube de posición para guardar otro nombre, siempre me muestra el primero.

Seguiré investigando a ver...

Última edición por Fernando_net; 21/11/2013 a las 15:59 Razón: En vez de strtoks había puesto struks
  #4 (permalink)  
Antiguo 21/11/2013, 16:17
 
Fecha de Ingreso: agosto-2012
Mensajes: 601
Antigüedad: 11 años, 8 meses
Puntos: 83
Respuesta: Problema con array, char y int

Te lo acabo de comentar, necesitas listas. Hace un par de dias en este foro apareció un tema sobre listas, tal vez te sea de ayuda.

Saludos
vosk
  #5 (permalink)  
Antiguo 21/11/2013, 16:24
 
Fecha de Ingreso: marzo-2010
Mensajes: 191
Antigüedad: 14 años, 1 mes
Puntos: 3
Respuesta: Problema con array, char y int

Cita:
Iniciado por vosk Ver Mensaje
Te lo acabo de comentar, necesitas listas. Hace un par de dias en este foro apareció un tema sobre listas, tal vez te sea de ayuda.

Saludos
vosk
Voy a ver si lo encuentro ;)

EDIT: Ya está, ya lo he encontrado. Voy a ver si lo implemento

De momento marco el tema como solucionado ;)

Un abrazo, Vosk!!

Última edición por Fernando_net; 21/11/2013 a las 16:30 Razón: Pongo el link
  #6 (permalink)  
Antiguo 22/11/2013, 03:28
 
Fecha de Ingreso: marzo-2010
Mensajes: 191
Antigüedad: 14 años, 1 mes
Puntos: 3
Respuesta: Problema con array, char y int

Hola de nuevo.

Pues lo he intentado implementar y solamente me muestra uno, no me muestra la lista entera de personas que han entrado al sistema.

Para tu información comentarte que he utilizado el método que he visto en el otro post que respondiste hace poco.

Este es el código:

Código C:
Ver original
  1. struct cliente {
  2.   char *nombre_cliente;
  3.   struct cliente *siguiente;
  4. };
  5.  
  6. void insertar(struct cliente **final, char *nombre){
  7.    struct cliente *cliente1, *cliente2;
  8.  
  9.    //reservo memoria para el nuevo dato
  10.    cliente1 = malloc(sizeof(struct client));
  11.    cliente1->nombre_cliente = nombre;
  12.    cliente1->siguiente = 0;
  13.  
  14.    //si la lista no existe, se asigna el nuevo dato como base
  15.    if(!(cliente2= *final)) {
  16.       *final = cliente1;
  17.    }
  18.    else {
  19.       //si la lista existe, se busca el ultimo elemento y se asigna el nuevo como siguiente
  20.       while(cliente2->siguiente) {
  21.          cliente2 = cliente2->siguiente;
  22.       }
  23.       cliente2->siguiente = cliente1;
  24.    }
  25. }
  26.  
  27. void muestra(struct cliente *lista) {
  28.     struct cliente *cliente1;
  29.  
  30.     //simplemente recorre la lista
  31.     cliente1 = lista;
  32.     while(cliente1) {
  33.         printf("%s\n", cliente1->nombre);
  34.         cliente1 = cliente1->siguiente;
  35.     }
  36. }

Y esto es el main()

Código C:
Ver original
  1. char *nom;
  2.    nom = strtok(mensaje, " ");
  3.    nom = strtok(NULL, " ");
  4.              
  5.    struct client *real;
  6.    real=0;
  7.              
  8.    insertar(&real,nom);
  9.              
  10.    muestra(real);
  #7 (permalink)  
Antiguo 22/11/2013, 13:12
 
Fecha de Ingreso: agosto-2012
Mensajes: 601
Antigüedad: 11 años, 8 meses
Puntos: 83
Respuesta: Problema con array, char y int

Como obtienes la variable 'mensaje'?

Debido a como asignas los datos (asignacion con punteros) todos los elementos tokenizados que envias a la lista estran ligados a la variable original: dejan de ser accesibles al finalizar el ambito de esa variable (scope). Dependiendo de como obtienes el texto de 'mensaje' tendras que modificar la asignacion y la variable de la estructura.


Código C:
Ver original
  1. char *nombre_global;
  2.  
  3. void funcion() {
  4.     nombre_global = "test";//asignas
  5.     //al finalizar esta funcion la memoria que reservo esta misma funcion para el texto 'test'
  6.     //deja de ser accesible, al igual que el texto que contiene
  7. }
  8.  
  9. int main() {
  10.     funcion();
  11.     //ahora nombre_global apunta a una direccion no accesible
  12.     //y da igual si es una unica variable como una lista
  13. }


Si quieres que los textos no pierdan el scope al cambiar de funciones tines que declarar un array de texto suficiente para copiar el texto dentro, y para asignar el texto tienes que copiarlo tal como te comenté con 'sprintf' o tal como encontraste con 'strcpy':

Código C:
Ver original
  1. struct cliente {
  2.      char nombre[100];
  3. };

Ahora en la funcion de añadir en vez de asignar el puntero copias el contenido:

Código C:
Ver original
  1. sprintf(cliente.nombre, nombre);

Con esto consigues guardar el contenido en una direccion de memoria diferente de la original, entonces por mucho que la original pierda el scope esta sigue siendo accesible.

Esto tiene un problema, que estas limitando el nombre del cliente a 99 caracteres (mas el \0). Puedes solucionarlo limitando el nº de caracteres copiados o puedes bloquear nueva memoria para el nombre:

Código C:
Ver original
  1. struct cliente {
  2.     char *nombre;
  3. };
  4.  
  5. void insertar(struct cliente **clt, char *nombre) {
  6.     struct cliente *nuevo;
  7.     int lon;
  8.  
  9.     nuevo = malloc(sizeof(struct cliente));
  10.  
  11.     lon = strlen(nombre);
  12.     nuevo->nombre = malloc(lon+1);
  13.     memset(nombre->cliente, 0, lon+1);
  14.     memcpy(nombre->clicnte, nombre, lon);//otra alternativa a sprintf y strcpy
  15.  
  16.     nuevo->siguiente = 0;
  17.  
  18.     etc...
  19. }

Esta solucion implica implementar mas controles de seguridad para garantizar que realmente se reserva la memoria solicitada, para cada un malloc se ha de verificar el retorno.

Resumen: la primera solucion consiste en asignar punteros (el codigo que colgaste), pero tienes que asegurarte que todas las variables que tokenizas estan presentes y no modificadas durante toda la aplicacion; la segunda solucion consiste en copiar el texto tokenizado a variables estaticas, esto implica que el tamaño del texto copiado depende de la capacidad de la variable estatica (esto implica que puede haber variables que no quepan y tengan que ser cortadas, pero tambien implica que haya variables para las que sobre espacio y la aplicacion reserve memoria inutil); la tercera opcion es copiar el texto tokenizado en variables de generacion dinamica, esto implica un mayor control durante la reserva de memoria pero tambien durante la liberacion.

En cualquier caso mientras usas listas recuerda que para cada malloc necesitas un free. Si reservas 4 estructuras cliente tienes que llamar 4 veces a free; si ademas reservas memoria dinamica para el nombre tambien tendras que liberar el nombre antes de liberar la estructura.

Aun otra cosa, tienes errores de tipeo en las variables (hay alguna que no deja compilar, otras compilan bien pero funcionan de casualidad). Revisa las declaraciones de las variables estructura (no generan error de compilacion pero no hacen lo que esperas), en la linea 5 tienes:

Código C:
Ver original
  1. struct client *real;

pero se da el caso que la estructura 'client' no fue declarada en ningun sitio (declaraste 'cliente', pero no 'client'). Al ser un puntero se interpreta como elemento nulo, es decir que podrias haber declarado void *real; y habría funcionado igual porque en la funcion 'insertar' reservas memoria para la estructura correcta.

Y una otra cosa: declara las variables para que se identifiquen lo maximo posible con lo que son o lo que significan:

Código C:
Ver original
  1. struct cliente *cliente1, *cliente2;

En el codigo tienes cliente1 y cliente2, cuando cliente1 actua de nuevo cliente y cliente2 actua de puntero de lectura, sería mejor darles el nombre de cliente_nuevo y puntero_lectura pero no para que compile bien sino para que sepas lo que estas haciendo, y a la vez cualquier otra persona que pueda ver tu codigo sepa de que va (imagina que el codigo se complica y tienes que pedir ayuda o simplemente estas trabajando en equipo, todos los que necesiten tener acceso al codigo deben saber a primera vista que estas haciendo, lo de usar variables casi iguales no ayuda).

Una ultima cosa, estas en c o en c++? Si estas en c++ olvidate de las listas de estructuras y de los char, aprende vectores y strings y seguramente lo solucionaras antes. Si estas en c++ y vas a usar objetos de c++ ten en cuenta que las funciones malloc y free no sirven, tendras que pasarlo todo a new y delete.

Y la ultima cosa, si te gustan las emociones fuertes puedes solucionarlo con un pool de memoria para textos separados por nulos, no es lo normal pero tambien es una solucion; esto se usa por ejemplo en la estructura de iniciacion de la funcion GetOpenFileName de la win32api, concretamente en el campo de filtros de extensiones, es decir que por muy raro que parezca lo usan todas las aplicaciones de windows programadas en win32 para ejecutar el dialogo de guardar como.

Saludos
vosk

Última edición por vosk; 22/11/2013 a las 13:19

Etiquetas: char, int, palabra, vector
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta

SíEste tema le ha gustado a 1 personas




La zona horaria es GMT -6. Ahora son las 20:02.