Ver Mensaje Individual
  #7 (permalink)  
Antiguo 22/11/2013, 13:12
vosk
 
Fecha de Ingreso: agosto-2012
Mensajes: 601
Antigüedad: 12 años, 3 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