none
¿como evitar subdesbordamiento? RRS feed

  • Pregunta

  • Creo que mi error es por subdesbordamiento. La aplicación programada en c++ consta de varias funciones. La principal recibe datos en un buffer de entrada/salida pasado por referencia a las funciones que llama. Posteriormente procesa esos datos. A veces el valor que necesito tarda más en ser calculado y por tanto en ser devuelto.  Aunque cuando compilo el proyecto y ejecuto la solución siempre me funciona correctamente, al crear un instalador y ejecturarlo desde fuera del entorno, a veces se cuelga el programa y se termina por cerrar. Siempre en ese mismo punto,  cuando tengo que recibir ese dato que tarda un poco mas. Quizas intente procesar los datos antes de haber terminado de recibirlos.

    Es algo que como veis  no pasa siempre.Rara vez me pasa en la primera ejecución, pero cuando me da por repetir el proceso una y otra vez, termina colgándose.  y no se a que puede ser debido. En el bloqueo del programa a veces me indica :

     acceso a memoria protegida

     entonces pienso que se pudo producir desbordamiento pero creo que tengo bien denifinida la longitud de los buffers y el tamaño de las variables que almaceno, de noser así creo que no funcionaría nunca. 

    Espero vuestra ayuda como siempre.

    P.D: todas las pruebas las hice en windows XP, pero al probar el ejecutable en windows vista, el programa SIEMPRE se cuelga en ese punto de demora de datos devueltos.

    Encima mi programa crea algunos archivos en el directorio donde fue instalada la aplicación, y ya sabemos lo que pasa en Vista:

    Error: acceso denegado al directorio de archivos de programa. y se cierra.

    Los ficheros son binarios creados con la instrucción:

    FileStream^ fs = gcnew FileStream("image.j2k", FileMode::Create);

    He pensado que podría crearlos en el directorio Temp ya que de todos modos al finalizar la aplicación ésta elimina esos ficheros, pero no se como referenciar un directorio que no sea donde se instala la aplicación.

    gracias.
    martes, 14 de septiembre de 2010 21:39

Respuestas

  • Si tu aplicación no usa hilos, y se sigue colgando como dices, seguro que tienes más fugas de memoria, y la únca manera es comprobarlo todo.

    Una forma de ver si no te peta en Debug pero sí suelta, es que compiles en Release generando símbolos de depuración (Opciones -> Linker -> Debugging -> Yes), y lo ejectues igual que en el modo Debug. La depuración será un poco psicodélica, pero te acercará al problema.

    Otra cosa sería habilitar el Just-In-Time debugging, para que cuando te pete, salte la pantalla de anexar a un debugger. Lo enganchas al Visual Studio y como tienes la info de debug, te saltará en la línea faltona. El problema es que no sé cómo se hace...

    Pero he encontrado esto, que podría valerte: http://www.codeproject.com/KB/exception/Locate_exception_position.aspx


    MVP Visual C++ - Visita mi blog sobre desarrollo: http://geeks.ms/blogs/rfog/
    • Marcado como respuesta Davidgen lunes, 27 de septiembre de 2010 16:34
    viernes, 17 de septiembre de 2010 15:45
    Moderador

Todas las respuestas

  • Para obtener el directorio temp puedes usar GetTempPath: http://msdn.microsoft.com/en-us/library/aa364992(v=VS.85).aspx o, para otros directorio interesantes, usar SHGetKnownFolderPath : http://msdn.microsoft.com/en-us/library/bb762188(v=VS.85).aspx.

    Para evitar el subdesbordamiento, es fácil: no leas nada hasta que los datos esten completos.

    ¿Cómo saber si están completos? utiliza otra variable bool, por ejemplo "bool DatosCompletos=false;". La parte que graba, sólo graba cuando está a false y la pone a true cuando están todos los datos grabados. La parte que lee no lee nada hasta que este a true y la pone a false cuando ya ha procesado todos los datos. Eso si sólo tienes una parte que graba y otra que lee. Si tienes varias partes que graban y/o leen tienes que buscar soluciones más sofisticadas con funciones de sincronización. En http://msdn.microsoft.com/en-us/library/ms686360(v=VS.85).aspx tienes donde escoger la que más se ajuste a tus necesidades.

    • Marcado como respuesta Davidgen jueves, 16 de septiembre de 2010 13:29
    • Desmarcado como respuesta Davidgen lunes, 27 de septiembre de 2010 16:34
    miércoles, 15 de septiembre de 2010 13:40
  • Para obtener el directorio temp puedes usar GetTempPath: http://msdn.microsoft.com/en-us/library/aa364992(v=VS.85).aspx  o, para otros directorio interesantes, usar SHGetKnownFolderPath : http://msdn.microsoft.com/en-us/library/bb762188(v=VS.85).aspx .

    Para evitar el subdesbordamiento, es fácil: no leas nada hasta que los datos esten completos.

    ¿Cómo saber si están completos? utiliza otra variable bool, por ejemplo "bool DatosCompletos=false;". La parte que graba, sólo graba cuando está a false y la pone a true cuando están todos los datos grabados. La parte que lee no lee nada hasta que este a true y la pone a false cuando ya ha procesado todos los datos. Eso si sólo tienes una parte que graba y otra que lee. Si tienes varias partes que graban y/o leen tienes que buscar soluciones más sofisticadas con funciones de sincronización. En http://msdn.microsoft.com/en-us/library/ms686360(v=VS.85).aspx  tienes donde escoger la que más se ajuste a tus necesidades.

    muchas gracias Bartomeu, redireccioné los ficheros de salida al directorio temp con GetTempPath:

    DWORD dwRetVal = 0;

    TCHAR lpTempPathBuffer[MAX_PATH];

    dwRetVal = GetTempPath(MAX_PATH,          // length of the buffer
                               lpTempPathBuffer);           // buffer for path

    //y ahora añado el el path temp al nombre del archivo y extensión que quiero crear:

    String ^ temp= gcnew System::String(lpTempPathBuffer);
    String ^ dir=Path::Combine(temp,"mifichero.j2k");

    //y ya tengo en dir la ruta completa, solo tengo que convertirla a char para poder utilizarla en mis //funciones:

    (char *)(void*)Marshal::StringToHGlobalAnsi(dir);

     

    En cuanto al subdesbordamiento apliqué lo que me dijiste, sin embargo me siguen saliendo errores de acceso a memoria protediga, y en distintos puntos , de forma aleatoria.

    gracias.

    jueves, 16 de septiembre de 2010 13:27
  • Posiblemente en XP también tengas el desbordamiento o lo que sea que te esté pasando, pero al no tener el nivel de seguridades de Vista/7, simplemente trague hasta que termine fallando de forma catastrófica en algún momento futuro.

    Lo que cuentas es típico de problemas de sincronización y/o fugas de memoria. Si estás usando hilos, asegurate de que tienes al menos secciones críticas o mutex en las partes adecuadas (cuando exista una posibilidad de que dos hilos lean/escriban en la misma variable a la vez).

    En general, cuando dentro del IDE la aplicación no explota pero fuera, sí, lo más común sea una fuga de memoria. Dependiendo del tipo de proyecto y versión, Visual C++ te las detecta de forma automática, pero no está de más que te asegures.

    Puedes empezar por leerte esto:  http://geeks.ms/blogs/rfog/archive/2010/07/01/detectando-fugas-de-memoria-en-visual-c.aspx


    MVP Visual C++ - Visita mi blog sobre desarrollo: http://geeks.ms/blogs/rfog/
    • Editado RFOGModerator jueves, 16 de septiembre de 2010 14:36 Corregir
    jueves, 16 de septiembre de 2010 14:36
    Moderador
  • Encontré en mi código algunas cadenas de tipo char* para las que no se reserva memoria ni se libera.

    Ejemplo:

    char * var1="hola mundo";

    char *var2="añadido";

    sprintf(var1+10,"%s",var2);

    pensé que no habría problema al hacer esta operación.

    jueves, 16 de septiembre de 2010 15:46
  • Eso en un compilador antiguo podría valer, aunque esté mal.

    Uno nuevo te pondrá "hola mundo" en la sección RO del segmento de datos (.rodata creo que se llama). En XP va en un área normal, y el DEP no se entera, pero en Vista/7 va marcado con RO y el DEP sí se entera, o debería enterarse.

    Luego var1 apuntará a dicha área de sólo lectura y cuando intentes cambiar algo no del puntero, sino de los datos a los que apunta, ¡plam! el aviso.

    La forma correcta, supongo que ya lo sabes, es:

    char var1[]="hola mundo";

    Así irá en la pila en lugar de en el .rodata.

     


    MVP Visual C++ - Visita mi blog sobre desarrollo: http://geeks.ms/blogs/rfog/
    jueves, 16 de septiembre de 2010 15:52
    Moderador
  • Puedes empezar por leerte esto:  http://geeks.ms/blogs/rfog/archive/2010/07/01/detectando-fugas-de-memoria-en-visual-c.aspx
    MVP Visual C++ - Visita mi blog sobre desarrollo: http://geeks.ms/blogs/rfog/

    Gracias RFOG, la dirección que me diste me  ha ayudado a encontrar 6 fugas y alguna de ellas con reserva de 3  megabytes en total o más. Ahora la aplicación ya solo me da el aviso en el punto donde realizo operaciones iguales a la del ejemplo que te puse, realizaré todos los cambios necesarios para dejarlo de la forma correcta y veré si tengo novedades o si solucioné todo. 

    Me sigue preocupando la parte donde empiezo a trabajar con ficheros pesados, imagenes de varios megas, ahí tengo definidas estructuras para poder trabajar de forma independiente con las cabeceras de imagen, etc... en ese punto a veces se sigue colgando la aplicación,y no sale ningún aviso de ningún tipo. A veces funciona, y de repente al tercer o cuarto intento deja de hacerlo.

    Creo que en algún punto me la he jugado asignando una cadena de bytes directamente a mi estructura, o con un simple memcpy, pero no se si esto pueda generar esos errores aleatorios. Voy a seguir analizando código no obstante a ver si consigo encontrar algo que pueda producir ese cuelgue. 

    Gracias a vuestra ayuda la aplicación ya es mucho más estable.

    viernes, 17 de septiembre de 2010 11:03
  • Si tu aplicación no usa hilos, y se sigue colgando como dices, seguro que tienes más fugas de memoria, y la únca manera es comprobarlo todo.

    Una forma de ver si no te peta en Debug pero sí suelta, es que compiles en Release generando símbolos de depuración (Opciones -> Linker -> Debugging -> Yes), y lo ejectues igual que en el modo Debug. La depuración será un poco psicodélica, pero te acercará al problema.

    Otra cosa sería habilitar el Just-In-Time debugging, para que cuando te pete, salte la pantalla de anexar a un debugger. Lo enganchas al Visual Studio y como tienes la info de debug, te saltará en la línea faltona. El problema es que no sé cómo se hace...

    Pero he encontrado esto, que podría valerte: http://www.codeproject.com/KB/exception/Locate_exception_position.aspx


    MVP Visual C++ - Visita mi blog sobre desarrollo: http://geeks.ms/blogs/rfog/
    • Marcado como respuesta Davidgen lunes, 27 de septiembre de 2010 16:34
    viernes, 17 de septiembre de 2010 15:45
    Moderador
  • Perdona que me meta, pero tanto si hace

    char * var1="hola mundo";

    como

    char var1[]="hola mundo";

    si después hace

    sprintf(var1+10,"%s",var2);

    estará escribiendo en una memoria que no tiene: en Var1+10 no sabe lo que hay, (y haya lo que haya no debe machacarlo aunque pueda). En el caso de memoria de sólo lectura dará error pero si lo hace en la pila, se estará cargando datos de la pila y probablemente pierda la dirección de vuelta, entre otros valores.

    Creo que lo correcto sería:

    char Buffer[UN_VALOR_ARBITRARIO_LO_SUFICIENTE_GRANDE];

    sprintf(Buffer,"%s%s",var1,var2);

    O, ya que se supone que está trabajando con C++,

    std::string var1("hola mundo");

    std::string var2("añadido");

    var1 += var2;

    PD.: Ya que en su ejemplo no inserta un espacio entre 'mundo' y 'añadido', yo tampoco lo he insertado en el mio

    PD2.: Si estamos en C++ por que no nos olvidamos de toda la familia de funciones printf.

    sábado, 18 de septiembre de 2010 18:01
  • Si, vale, cierto, pero suponiendo que var1[] tuviera espacio, es vá lido  
    hacerlo.
     
    El primer sprintf creo que no es necesario, ya lo hace el compilador por   
    ti...
     On Sat, 18 Sep 2010 20:01:02 +0200, <Bartomeu> wrote:
     
    > Perdona que me meta, pero tanto si hace
    >
    > char * var1="hola mundo";
    >
    > como
    >
    > char var1[]="hola mundo";
    >
    > si después hace
    >
    > sprintf(var1+10,"%s",var2);
    >
    > estará escribiendo en una memoria que no tiene: en Var1+10 no sab e lo  
    > que hay, (y
    > haya lo que haya no debe machacarlo aunque pueda). En el caso de memor ia  
    > de sólo
    > lectura dará error pero si lo hace en la pila, se estará car gando datos  
    > de la
    > pila y probablemente pierda la dirección de vuelta, entre otros v alores.
    >
    > Creo que lo correcto sería:
    >
    > char Buffer[UN_VALOR_ARBITRARIO_LO_SUFICIENTE_GRANDE];
    >
    > sprintf(Buffer,"%s%s",var1,var2);
    >
    > O, ya que se supone que está trabajando con C++,
    >
    > std::string var1("hola mundo");
    >
    > std::string var2("añadido");
    >
    > var1 += var2;
    >
    > PD.: Ya que en su ejemplo no inserta un espacio entre 'mundo' y  
    > 'añadido', yo
    > tampoco lo he insertado en el mio
    >
    > PD2.: Si estamos en C++ por que no nos olvidamos de toda la familia de   
    > funciones
    > printf.
     -- 
    Microsoft Visual C++ MVP => http://geeks.ms/blogs/rfog
    ======================== =============== La verdad es lo que es, y sigue siendo verdad aunque se piense al rev� �s.
     -- Antonio Machado. (1875-1939) Poeta español.
     

    MVP Visual C++ - Visita mi blog sobre desarrollo: http://geeks.ms/blogs/rfog/
    domingo, 19 de septiembre de 2010 9:15
    Moderador
  • Si, vale, cierto, pero suponiendo que var1[] tuviera espacio, es válido  
    hacerlo.
     

    Yo dígo que no es válido. Estás presuponiendo que el compilador dejará espacio detrás. Si lo defines como char var1[]="hola mundo"; por definición, valga la redundancia, no tienes espacio. El estandar te asegura que expandirá el [] para que quepa el entrecomillado mas el '\x0' final. No dice nada del espacio que dejará el compilador detrás.

    Y aunque en esta versión del compilador, por redondeo de memoria a posiciones pares o cuadruples, tuvieras por casualidad suficiente sitio, no puedes asegurar que otra versión de este mismo compilador, u otro compilador distinto, trate igual el redondeo de memoria.

    Un programa que se basa en suposiciones de lo que hay detrás de algo es candidato a multiples errores que pueden ir de lo curioso a lo catastrófico. Que es lo que le pasa precisamente a Davidgen.

    domingo, 19 de septiembre de 2010 11:23
  • Bartomeu, que estamos diciendo lo mismo.
     
    Con "si tuviera espacio" me refiero a eso mismo, que la declaración  de  
    var1[] fuera más grande que ese offset de 10.
     
    Evidentemente var1[]="hola mundo" No tiene espacio, pero var1[]="hol a  
    mundo                      ", sí.
     
    Pensaba que me había explicado bien.
     
    On Sun, 19 Sep 2010 13:23:44 +0200, <Bartomeu> wrote:
     
    > Si, vale, cierto, pero suponiendo que var1[] tuviera espacio, es vá lido
    >
    > hacerlo.
    >
    >
    > Yo dígo que no es válido. Estás presuponiendo que el co mpilador dejará  
    > espacio
    > detrás. Si lo defines como char var1[]="hola mundo"; por defini ción,  
    > valga la
    > redundancia, no tienes espacio. El estandar te asegura que expandir� � el  
    > [] para
    > que quepa el entrecomillado mas el '\x0' final. No dice nada del espac io  
    > que
    > dejará el compilador detrás.
    >
    > Y aunque en esta versión del compilador, por redondeo de memoria  a  
    > posiciones
    > pares o cuadruples, tuvieras por casualidad suficiente sitio, no puede s  
    > asegurar
    > que otra versión de este mismo compilador, u otro compilador dist into,  
    > trate
    > igual el redondeo de memoria.
    >
    > Un programa que se basa en suposiciones de lo que hay detrás de a lgo es
    > candidato a multiples errores que pueden ir de lo curioso a lo  
    > catastrófico. Que
    > es lo que le pasa precisamente a Davidgen.
     -- 
    Microsoft Visual C++ MVP => http://geeks.ms/blogs/rfog
    ======================== =============== La humanidad está ante una bifurcación histórica. Un cami no lleva a la  
    desesperación y a la renuncia total. El otro, a la extinción d efinitiva.  
    Roguemos tener la sabiduría que hace falta.
     -- Woody Allen. (1935-) Actor, director y escritor estadounidense.
     

    MVP Visual C++ - Visita mi blog sobre desarrollo: http://geeks.ms/blogs/rfog/
    domingo, 19 de septiembre de 2010 13:06
    Moderador
  • Bartomeu, que estamos diciendo lo mismo.
     
    Con "si tuviera espacio" me refiero a eso mismo, que la declaración  de  
    var1[] fuera más grande que ese offset de 10.
     
    Evidentemente var1[]="hola mundo" No tiene espacio, pero var1[]="hol a  
    mundo                      ", sí.
     
    Pensaba que me había explicado bien.
     
    Lo siento, pero no te entendí. :-(
    domingo, 19 de septiembre de 2010 13:52
  • Bartomeu dijo el sábado, 18 de septiembre de 2010 18:01:

    PD2.: Si estamos en C++ por que no nos olvidamos de toda la familia de funciones printf.

     

    La verdad es que tienes razón, pero estoy aprovechando un proyecto de otros compañeros como base, y está programado en C. Por lo pronto sigo modificando únicamente los accesos a memoria no reservada que voy encontrando. Mañana haré pruebas de nuevo a ver que tal sigue todo.

    gracias.

    domingo, 19 de septiembre de 2010 19:35
  • Lo cierto es que odio el C teniendo disponible C++, y es algo que siempr e  
    recomiendo: el compilador de C++, aunque sea usado como C, te va a pilla r  
    más de una pifia él solo...
     
    Y muchas veces me veo abocado a usar C porque no hay otra cosa para la  
    plataforma en la que esté desarrollando... Imaginaros un motor gr� �fico 2D  
    hecho en C.
     
    Pues eso.
     
    On Sun, 19 Sep 2010 21:35:11 +0200, <Davidgen> wrote:
     
    > Bartomeu dijo el sábado, 18 de septiembre de 2010 18:01:
    >
    > PD2.: Si estamos en C++ por que no nos olvidamos de toda la familia de   
    > funciones
    > printf.
    >
    > La verdad es que tienes razón, pero estoy aprovechando un proyect o de  
    > otros
    > compañeros como base, y está programado en C. Por lo pronto  sigo  
    > modificando
    > únicamente los accesos a memoria no reservada que voy encontrando .  
    > Mañana haré
    > pruebas de nuevo a ver que tal sigue todo.
    >
    > gracias.
     -- 
    Microsoft Visual C++ MVP => http://geeks.ms/blogs/rfog
    ======================== =============== La humanidad está ante una bifurcación histórica. Un cami no lleva a la  
    desesperación y a la renuncia total. El otro, a la extinción d efinitiva.  
    Roguemos tener la sabiduría que hace falta.
     -- Woody Allen. (1935-) Actor, director y escritor estadounidense.
     

    MVP Visual C++ - Visita mi blog sobre desarrollo: http://geeks.ms/blogs/rfog/
    domingo, 19 de septiembre de 2010 19:52
    Moderador
  • gracias por toda la ayuda, ya conseguí corregir todos los errores.  Con este proyecto he aprendido como pequeñas cosas pueden liarla a lo grande. Hay algo que no entiendo y aunque no tenga nada que ver con la pregunta incial de este tema, si está relacionado con los errores que provocaban el cuelgue.  Pondré el ejemplo más sencillo . Se trata del operador sizeof() que me devuelve valores no esperados:

    struct tipo1{

    char var1[4];

    char var2[4];

    uint32 var3;

    uint16 var4;

    };

    //los valores que me devuelve sizeof() son:

    sizeof(var1)= 4

    sizeof(var2)=4

    sizeof(uint32)=4

    sizeof(uint16)=2

    sizeof(struct tipo1)=16 ¿? Esto me obliga a analizar el cógido y donde utilize este valor añadirle -2;

    algo poco coherente y muy pesado de aplicar a todo el código, aunque sea válido para obtener el resultado esperado.

     

    lunes, 27 de septiembre de 2010 16:42
  • Cuando declaras una especificación binaria y para ello utilizas una estructura, lo clásico es forzar al compilador a alinear a 1 byte.

    Vas a la doc y buscas en la sección de pragmas (generalmente está allí) el comando requerido. Si mal no recuerdo se llama pack y normalmente haces un push de la alineación deseada, declaras la estructura y popeas para recuperar el alineamiento previo.


    gracias
    lunes, 27 de septiembre de 2010 20:59