Duda strings, punteros y errores "string alloc: out of memory"

Started by Arcontus, January 19, 2018, 12:26:54 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Arcontus

Hola chicos,

El caso es que he tenido una serie de errores "string alloc: out of memory" en mi proyecto, he leido varios temas al respecto de utilizar punteros con strings y no me queda claro si los estoy utilizando correctamente.

Para comenzar, algunos procesos tenían declaradas arrays de strings (string str[4]), esto lo he cambiado a strings únicas (str1, str2, ...). Entiendo que esto estaba mal, ¿correcto? y diría sin estar aun seguro al 100% que esto ha solucionado el problema del "string alloc: out of memory" en mi proyecto.

Por otro lado utilizo una serie de funciones para trabajar con cadenas de texto y querría descartar con vuestra ayuda que estuviera implementando el uso de punteros de string de manera incorrecta.

La idea es tener un proceso "A" con 2 strings A1 y A2, ¿puede llamar "A" a una funcion pasando los punteros de ambas strings (&A1, &A2) para que se modifique una de ellas dentro de esa funcion?

Os cuelgo una de mis funciones como ejemplo:


FUNCTION split_string(string * mensaje, string *str_valor, int seccion) //Dado una cadena mensaje con "cadena1|cadena2" devuelve el string apropiado sobre str_valor segun seccion proporcionada.
PRIVATE
    int indice, inicio, marca, tam;
END
BEGIN
    tam = len((*mensaje));
    if (tam > 0)
        marca = find((*mensaje),"|");
        if (marca > -1)
            if (seccion== 0)
                (*str_valor) = substr((*mensaje),0,marca);
            else
                (*str_valor) = substr((*mensaje),marca+1, tam);
            END
        END
    END
END


Gracias de antemano,
Un saludo!
5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com

SplinterGU

las strings no pueden usarse con punteros, son objetos que tienen un contador de uso, y si es un puntero, no se sabe si el puntero apunta a un identificador de string o a un area con basura o sin alocar.

(revisare nuevamente el sistema a ver si puedo implementar algo al respecto... aunque creo que no es seguro...)
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Arcontus

Hola Splinter, no acabo de entender lo que comentas de que no pueden usarse, al menos la funcion "write_string" si usa el puntero para algo y entiendo que no da problemas.
Te agradecería enormemente que me dieras más detalle de como funciona string y porque a veces apunta a basura y el mecanismo que usa write_string para no petar.

Gracias!
5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com

SplinterGU

el tema es, las string son en realidad un numero entero representa el indice dentro de una tabla de strings, y donde ademas hay un contador de usos... y cuando este contador de usos es 0, la string se libera (es un garbage collector), en el caso de los punteros, el motor no puede saber si *puntero_a_string, esta apuntando a una string o todavia no fue asignado a nada, entonces el motor no actualiza el contador de usos ahi...

cuando tu haces un &var_string, lo que haces es apuntar a la direccion de la variable que contiene el id (indice) de la string real, esta direccion no cambia nunca... y esta apuntando realmente a una string real, con un espacio previamente reservado por el motor y con un contador de usos controlado.
ahora cuando tu pasas libremente punteros a string, estos inicialmente no apuntan a un area estatica, apuntan a lo que aloques, a menos que tu puntero sea un puntero a una variable ya declarada como string... pero como sea, el motor no controla el contador de usos con punteros, porque no sabe que realmente esta usado y que no... por ende, puede ocurrir que cuando tu puntero quiera acceder a la string, el puntero realmente este apuntando a una string que ya no existe, puesto que se elimino por el contador de usos.

por eso, a menos que sepas que estas haciendo, no es recomendable usar punteros a string.

es dificil de explicar, asi que puede que tambien sea dificil de entender...
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

SplinterGU

agrego, o puede que este apuntando a otra area de memoria diferente... por ejemplo, si usas realloc...

cuando se sale de una funcion, toda string que su contador de uso sea igual a 0, es eliminada.
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Arcontus

Quote from: SplinterGU on January 19, 2018, 08:27:04 PM
agrego, o puede que este apuntando a otra area de memoria diferente... por ejemplo, si usas realloc...

cuando se sale de una funcion, toda string que su contador de uso sea igual a 0, es eliminada.

Entiendo que eso que comentas también sucede con los write_strings, de hecho con cualquier write_*, si el proceso que creo la variable referenciada por write_* muere, o se hace un delete_text() o petada al canto (como es normal).

En mi caso uso esos punteros de la siguiente manera:


FUNCTION split_string(string * mensaje, string *str_valor, int seccion) //Dado una cadena mensaje con "cadena1|cadena2" devuelve el string apropiado sobre str_valor segun seccion proporcionada.
PRIVATE
    int indice, inicio, marca, tam;
END
BEGIN
    tam = len((*mensaje));
    if (tam > 0)
        marca = find((*mensaje),"|");
        if (marca > -1)
            if (seccion== 0)
                (*str_valor) = substr((*mensaje),0,marca);
            else
                (*str_valor) = substr((*mensaje),marca+1, tam);
            END
        END
    END
END

PROCESS a()
PRIVATE
    string mistring1 = "Hola|Adios", mistring2;
    int estado;
END
BEGIN
    split_string(&mistring1, &mistring2, estado);
    write_string(0,200,200,0,&mistring2);
    LOOP
        FRAME;
        if (key(_space))
            while(key(_space))
                FRAME;
            END
            if (estado == 0)
                estado = 1;
            else
                estado = 0;
            END
            split_string(&mistring1, &mistring2, estado);
        END
        FRAME;
    END
END


Proces A() crea 2 strings mistring1y mistring2 en su seccion private, y llama a la funcion  split_string() para obtener una de las secciones previamente asignadas.
Si he entendido correctamente, esto que pretendo es un uso correcto de los punteros de string, por que no se utiliza realloc ni malloc ni nada por el estilo para crearlos.

¿SplinterGU code approved?
;D ;D

Gracias por vuestra ayuda,
Un saludo!
5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com

Drumpi

Yo en su día tuve millones de problemas con los array de strings.
No conocía exactamente el funcionamiento interno de las strings, suponía que eran un array de memoria dinámica que se agrandaba y encogía en función del número de caracteres, y que por la alineación de memoria de los propios array, provocaba que los espacios de memoria se empezaran a pisar unos a otros.

Así que me definí un tipo "string_node" tal como:
type string_node
    string cadena;
    string_node pointer sig
end

Y luego me creé la tipica lista enlazada de nodos, con funciones para añadir, eliminar nodos, buscar, recorrer, vaciar, etc, y la llamé "string_class". Sí, básicamente emulaba la clase String de Java que estaba estudiando en aquel momento.
Se resolvieron TODOS los problemas. Splinter insiste que eso debería fallar en algún momento, pero debe andar ya por los 7 años sin excepciones de ningún tipo.

El código lo subí al foro. http://forum.bennugd.org/index.php/topic,2208
Creo que tuve que arreglar un par de cosillas desde entonces, pero más fallos míos que "malas prácticas" :D
Mi principal uso: crear un fichero de texto con frases y cargarlas todas en memoria, ideal para almacenar diálogos o los textos en el idioma que se elija para el programa.
Hala, como con 1001 procesos sólo va a 9 FPS, vamos a meterle 32 veces más, a ver si revienta.
(Drumpi epic moment)

SplinterGU

Quote from: Arcontus on January 19, 2018, 09:13:51 PM
Quote from: SplinterGU on January 19, 2018, 08:27:04 PM
agrego, o puede que este apuntando a otra area de memoria diferente... por ejemplo, si usas realloc...

cuando se sale de una funcion, toda string que su contador de uso sea igual a 0, es eliminada.

Entiendo que eso que comentas también sucede con los write_strings, de hecho con cualquier write_*, si el proceso que creo la variable referenciada por write_* muere, o se hace un delete_text() o petada al canto (como es normal).

En mi caso uso esos punteros de la siguiente manera:


FUNCTION split_string(string * mensaje, string *str_valor, int seccion) //Dado una cadena mensaje con "cadena1|cadena2" devuelve el string apropiado sobre str_valor segun seccion proporcionada.
PRIVATE
    int indice, inicio, marca, tam;
END
BEGIN
    tam = len((*mensaje));
    if (tam > 0)
        marca = find((*mensaje),"|");
        if (marca > -1)
            if (seccion== 0)
                (*str_valor) = substr((*mensaje),0,marca);
            else
                (*str_valor) = substr((*mensaje),marca+1, tam);
            END
        END
    END
END

PROCESS a()
PRIVATE
    string mistring1 = "Hola|Adios", mistring2;
    int estado;
END
BEGIN
    split_string(&mistring1, &mistring2, estado);
    write_string(0,200,200,0,&mistring2);
    LOOP
        FRAME;
        if (key(_space))
            while(key(_space))
                FRAME;
            END
            if (estado == 0)
                estado = 1;
            else
                estado = 0;
            END
            split_string(&mistring1, &mistring2, estado);
        END
        FRAME;
    END
END


Proces A() crea 2 strings mistring1y mistring2 en su seccion private, y llama a la funcion  split_string() para obtener una de las secciones previamente asignadas.
Si he entendido correctamente, esto que pretendo es un uso correcto de los punteros de string, por que no se utiliza realloc ni malloc ni nada por el estilo para crearlos.

¿SplinterGU code approved?
;D ;D

Gracias por vuestra ayuda,
Un saludo!

me parece que no es correcto, mañana lo reviso bien, reviso el codigo y te respondo...
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

SplinterGU

Quote from: Drumpi on January 20, 2018, 01:31:13 AM
Yo en su día tuve millones de problemas con los array de strings.
No conocía exactamente el funcionamiento interno de las strings, suponía que eran un array de memoria dinámica que se agrandaba y encogía en función del número de caracteres, y que por la alineación de memoria de los propios array, provocaba que los espacios de memoria se empezaran a pisar unos a otros.

Así que me definí un tipo "string_node" tal como:
type string_node
    string cadena;
    string_node pointer sig
end

Y luego me creé la tipica lista enlazada de nodos, con funciones para añadir, eliminar nodos, buscar, recorrer, vaciar, etc, y la llamé "string_class". Sí, básicamente emulaba la clase String de Java que estaba estudiando en aquel momento.
Se resolvieron TODOS los problemas. Splinter insiste que eso debería fallar en algún momento, pero debe andar ya por los 7 años sin excepciones de ningún tipo.

El código lo subí al foro. http://forum.bennugd.org/index.php/topic,2208
Creo que tuve que arreglar un par de cosillas desde entonces, pero más fallos míos que "malas prácticas" :D
Mi principal uso: crear un fichero de texto con frases y cargarlas todas en memoria, ideal para almacenar diálogos o los textos en el idioma que se elija para el programa.

drumpi, ahi no estas usando punteros a strings... igual creo que tampoco es correcto, aunque depende de otras cosas...

el punto esta en el contador de usos de las strings... si llegan a 0, se eliminan de la memoria...
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Arcontus

SplinterGU como siempre tiene razón, la función "split_string" que he escrito es inestable. La mayoría del tiempo funciona como es debido pero no el 100% del tiempo, lo que provoca que se cierre el proyecto de manera inesperada, a veces si, otras no.

Ahora me gustaría saber:
A: Que es lo que está mal de la función. Así aprendemos todos...
B: Cual es la manera correcta de dado 1 string con "cadena1|cadena2" obtener en otra string el valor de la sección correspondiente.
C: ¿Se podría implementar algo como "exists_string()" para acceder con seguridad a la zona de memoria u otro tipo de mecanismo fiable?

Gracias de antemano,
Un saludo!
5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com

SplinterGU

podes usar arrays de bytes o char (no recuerdo cual de los 2)

byte * (o char *, probalo), eso es seguro, siempre y cuando hagas el alloc pertinente.

Download Lastest BennuGD Release: http://www.bennugd.org/node/2

SplinterGU

he estado analizando el codigo, debuguando y probando... y no, no tenia razon...

veo que si esta soportado punteros a strings de forma correcta.

o lo corregi en algun momento, o algo paso, en verdad no recuerdo haberlo corregido, pero veo que funciona correcto.

si esto no funciona es porque hay algun bug en algun lugar del motor...

pero tu funcion es correcta... y debugueandola a nivel C, esta todo bien el uso de strings.

aunque no necesitas pasar el primer parametro como puntero, puedes pasarlo como string tranquilamente.

igual tengo que probar completamente el sistema, quizas en algun caso especifico falla, pero con retornos por puntero va muy bien.
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Arcontus

Hasta mañana no podre hacer mas pruebas, ya que hoy no dispongo de ordenador, pero en los debugs que hice del programa, en un caso se cerro subitamente dentro de la funcion split_string() al asignar un valor a uno de los punteros.
En otro caso fue justo la siguiente instruccion, desde el proceso que invoca a split_string() al asignar un nuevo valor a la string que le paso a split_string().

No puedo descartar que sea otra cosa la que esta afectando, aun que me he revisado el codigo un monton de veces. De momento la unica pista que tengo es un pete de string alloc:out of memory y el registro de la ultima instruccion que se ejecuto en modo debug antes de petar el programa.

La verdad es que estoy tremendamente ofuscado con este problema.

5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com

SplinterGU

igualmente, tras dar muchas vueltas abrazado a mi almohada y entre soñar y pensar en los datos que recolecte de las pruebas de hace unas horas, parece que efectivamente hay al menos un problema con las strings, que explicare cuando lo tenga bien confirmado, y creo que ahi se fundamenta lo que dije anteriormente... sin embargo, tu funcion de string parece funcionar correctamente...

pregunta, estas usando por casualidad punteros a strings creados con alloc (mem_alloc, mem_calloc, etc)?

PD: debo confesar que dormi solo 3 horas, este tema de las strings no me dejo dormir... :P
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Arcontus

Quote from: SplinterGU on January 21, 2018, 11:57:03 AM
igualmente, tras dar muchas vueltas abrazado a mi almohada y entre soñar y pensar en los datos que recolecte de las pruebas de hace unas horas, parece que efectivamente hay al menos un problema con las strings, que explicare cuando lo tenga bien confirmado, y creo que ahi se fundamenta lo que dije anteriormente... sin embargo, tu funcion de string parece funcionar correctamente...

pregunta, estas usando por casualidad punteros a strings creados con alloc (mem_alloc, mem_calloc, etc)?

PD: debo confesar que dormi solo 3 horas, este tema de las strings no me dejo dormir... :P

Si te soy sincero, a mi tampoco me deja dormir tranquilo. El viernes tuve pesadillas con esto, y no es broma  ;D ;D ;D
No, ya he leido que no hay que crear punteros con mem_alloc, etc, la manera que uso para crear los strings es siempre desde la seccion PRIVATE de un proceso.

He cambiado una funcion que efectivamente estaba mal en mi proyecto y tiene numeros de ser la causante de todos estos problemas... Esa funcion no tiene nada que ver con strings, modifica un int *var al finalizarse, pero dicho puntero en algunos casos podría haber dejado de existir y por ello apuntar a una zona de memoria que no tocara, provocando después el fallo en otro lado del programa..., como no estoy seguro al 100% de haber hallado con el problema continuo investigando.

Gracias por las respuestas,
Un saludo!
5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com