2011-01-01 15 views
5

He buscado una respuesta a esto pero no puedo encontrar una. (Tengo una experiencia bastante limitada con C++)Comprobación para ver si se puede liberar un puntero

En mi biblioteca, libero una cadena. (Increíble, ¿eh?)

Aquí es donde surge el problema. Tengo una estructura que contiene un char * que puede asignarse en el montón o no. Si bien es un puntero válido, no se puede liberar.

IE

char* s1 = "A String"; 
char* s2 = (char*)memcpy(malloc(9), s1, 9); 

free(s2); 
free(s1); 

causará un error en el "libre (S1);" (Como debería) Dado que s1 en realidad no necesita para liberarse, (No está en el montón) ¿cómo puedo manejar esto de una manera "aceptable"? (En temas similares, la respuesta de "déjalo colapsar" no parecía razonable IMO)

Dado que la estructura no es únicamente creada por la biblioteca, no es posible garantizar que una cadena se copiará correctamente utilizando algo como memcpy.

Como esta es una biblioteca de Windows, no tengo que preocuparme por usar material ISO C o funciones C estándar.

+0

Mejor respuesta (en mi humilde opinión): Utilice un puntero opaco para garantizar que el 'struct' _es_ exclusivamente creado en la biblioteca. (Ofrecería más ayuda si usé Windows. Lo siento +1). –

+0

Gracias, si lo peor llega a ser peor, puedo hacerlo o usar SEH (aunque esto último me haría sentir que estaba haciendo algo mal) – James

+0

Al ver que se trata de una biblioteca de Windows, convierte el argumento en un BSTR. Luego requiere que el usuario lo asigne correctamente (con 'SysAllocString') y se garantiza que usará un separador correspondiente. Otros métodos son simplemente ... malos. Si su usuario tiene un compilador diferente, entonces no puede 'libre()' la cadena incluso si utilizaron 'malloc' –

Respuesta

2

Al tratarse de una biblioteca de Windows, establezca el argumento en BSTR. Luego necesita que el usuario lo asigne correctamente (con SysAllocString) y tiene la garantía de usar a matching deallocator.

Otros métodos son simplemente ... malos. Si su usuario tiene un compilador diferente, entonces no puede free() la cadena incluso si utilizaron malloc.

[Nota: Construcción de un comentario a petición de James, esto realmente es sólo un caso específico para Windows de la última de his suggestions]

nota además: BSTR es Unicode. De alguna manera recuerdo haber visto una forma de usar el asignador BSTR para almacenar cadenas ANSI, parece que SysAllocStringByteLen hace eso, pero ten en cuenta que poner datos ANSI en un BSTR será altamente contrario a cualquiera que esté familiarizado con BSTR.

+0

Muy bien, pero parece recordar que tuve algunas malas experiencias con bstr cuando tuve que usarlo (y SAFEARRAY) al tratar con el código .NET desde C++ nativo. Eso está fuera de tema, pero lo intentaré. – James

+0

@James: sospecho que fue el código COM el que causó la dificultad y no el BSTR. Las funciones BSTR son en realidad muy simples y fáciles de usar desde C o C++. Creo que VC++ incluso proporciona una clase contenedora para liberar automáticamente el BSTR a medida que sale del alcance, pero no agrega mucha funcionalidad y no saber exactamente cómo funciona me molestó, así que siempre llamé 'SysAllocString' y' SysFreeString 'directamente. –

6

En C++, no debe preocuparse por esto en absoluto. Use std::string y haga que administre la memoria automáticamente. Don't manage memory manually.

Si tuviera que hacerlo de forma manual, que tendría que manejar el recurso a sí mismo, por

  • hacer que el usuario de la biblioteca de gestionar la memoria a sí mismo, o
  • que el usuario tenga que decirte cómo administrar la memoria, o
  • indicando al usuario cómo va a administrar la memoria y luego esperar que el usuario cumpla.
+1

Este es un gran consejo, excepto por el hecho de que es (1) una biblioteca (2) en Windows y (3) aparentemente precompilada, según los comentarios. Y 'std :: string' se considera inseguro al pasar los límites del módulo en Windows. Siguiendo el patrón de la API de Win32, la primera viñeta que ofrece es, con mucho, la preferida por las bibliotecas de Windows para manejar la memoria. –

+0

@Ben: Programación de Windows: no es mi fuerte (gracioso, supongo). Tienes razón, por supuesto: si no puedes controlar el proceso de compilación tanto para la biblioteca como para los enlaces en su contra, esto sería bastante problemático. Rescindiría su solución BSTR si la publicara como respuesta. Esa es la solución más razonable, IMO. –

0

Puede malloc char *s1 y colocar "A String" como valor. Después de eso, puede liberar s1.

+0

Lo sé, pero si alguien va struct-> val = "random"; luego tengo problemas de nuevo – James

0

Este es el tipo de cosa que se conoce en tiempo de compilación. Miras el código y sabes qué liberar y qué no. Así que no piense en una forma de diferirlo al tiempo de ejecución, porque aparte del hecho de que no encontrará una forma en este caso, es la forma incorrecta de hacer las cosas en C++. Cuando puedes hacerlo estáticamente, hazlo estáticamente.

Utilice el sistema de tipos. Usa RAII

+0

El problema es que no se sabe en __my__ compilación, solo para las personas que realmente llaman a mi biblioteca. Un puntero es solo un número. – James

+1

@James, esto se trata de ** propiedad **. La pregunta que debe hacerse es a quién pertenece el puntero. Esto es algo que dices en la documentación de la función. De cualquier manera, la persona que llama no debe verificar si debe liberar el puntero o no. De nuevo, esta es información estática sobre la que debe tener claro. – wilhelmtell

0

Almacena todas las cadenas en el montón, entonces sabrás que todas necesitan ser liberadas. Si la cadena existe como en la memoria global, cópiela en un buffer de pila.

+0

¿De quién es el montón? En Windows, cada biblioteca tiende a tener su propio montón incompatible de malloc/free. Las funciones API de Win32 'HeapAlloc' y' HeapFree' funcionan a través de los límites del módulo, pero no son compatibles con malloc proporcionado por CRT y libres en cualquiera de los módulos. –

+0

El montón utilizado por malloc(), según cómo el OP asignó la cadena que se almacenó en el montón. –

0

He hecho algo similar en el pasado, donde usaría una cadena asignada por la pila en algunos casos, y una cadena asignada en el montón en otros, pero en última instancia pasarían a algún otro código común.

Lo que he hecho en ese caso, es tener dos punteros. Uno es NULL o una cadena asignada en el montón. El otro puntero apunta a la memoria asignada o a la misma memoria asignada en el montón como la primera. Cuando vaya a hacer su free(), solo verifica el puntero anterior.

Por supuesto, eso se verá desagradable en una API expuesta. En mi caso, era solo código interno.

Cuestiones relacionadas