2010-03-20 21 views
7

Siempre estoy confundido acerca de devolver un literal de cadena o una cadena desde una función. Me dijeron que podría haber pérdida de memoria porque no sabes cuándo se borrará la memoria.Cómo devolver un literal de cadena desde una función

Por ejemplo, en el siguiente código, ¿cómo implementar foo() para que la salida del código sea "Hello World"?

void foo (  )    // you can add parameters here. 
{ 

} 

int main() 
{ 
    char *c; 
    foo ( ); 
    printf ("%s",c); 
    return 0; 
} 

Además, si el tipo de retorno de foo() no es nula, pero se puede volver char*, lo que debería ser?

+0

Su requisito de que devuelva 'char *' en lugar de 'char const *' nos obliga a suponer que un literal de cadena simple no será suficiente y por lo tanto 'devolver" hello world ";' es una solución no válida. ¿Cuáles son sus requisitos sobre constness? –

Respuesta

9

Supongo que no podemos modificar main. Para obtener su trabajo programa sin una fuga, es necesario tener algo de almacenamiento estático:

void foo(char*& pC) // reference 
{ 
    static char theString[] = "thingadongdong"; 

    pC = theString; 
} 

Pero en realidad, esto no es un código muy convencional C++. Usted se utiliza std::string y std::cout, por lo que no tienen que preocuparse acerca de la memoria:

std::string foo(void) 
{ 
    return "better thingadongdong"; 
} 

int main(void) 
{ 
    // memory management is done 
    std::cout << foo() << std::endl; 
} 

Si usted se pregunta si algo necesita ser desasignado manualmente, se está haciendo mal.

+0

No siempre. Después de todo, std :: string realiza la asignación de forma manual. –

+1

Eh, cuando no estás en el "modo de escritura de la biblioteca" debería decir. :) – GManNickG

+0

Ahí vamos. Mucho mejor. Mi opinión exactamente –

2

Algo como esto:

void foo(char ** pChar) 
{ 
    // Make sure the string is shorter 
    // than the buffer 
    *pChar = new char[256]; 
    strcpy(*pChar,"Hello World!"); 
} 

A continuación, llame así:

foo(&c); 

Como se menciona en el comentario, tenga cuidado de la cadena que se está almacenando es menor que el búfer o se obtendrá un ... desbordamiento de pila! (Pun previsto)

+0

Y si hago 'foo (0)'? – GManNickG

+1

No se puede eliminar la referencia '0' Eso bloqueará su programa. –

+0

por qué es necesario puntero a puntero? ¿Por qué no solo puntero? – skydoor

4

Siempre estoy confundido acerca de devolver un literal de cadena o una cadena de una función.

inmutable, cadena literal

lo que tengo entendido, que son seguros para devolver una cadena literal directamente si el tipo de retorno es declarado const, para declarar que la cadena no está destinado a ser alterado. Esto significa que no necesita preocuparse por la vida útil de las fugas de cadena/memoria.

mutable cadena, no literal

Sin embargo, si necesita una cadena que puede cambiar en el lugar, es necesario tener en cuenta la vida útil de la cadena y el tamaño de la asignación de memoria en la que se está almacenado. Esto se convierte en un problema, ya que ya no puede devolver alegremente la misma memoria que contiene cadena para cada invocación de la función, ya que un uso anterior podría haber alterado el contenido de esa memoria, y/o todavía puede estar en uso. Por lo tanto, se debe asignar una nueva pieza de memoria para mantener la cadena devuelta.

Aquí es donde se produce la posibilidad de una fuga, y donde se debe elegir dónde debe ocurrir la asignación y la desasignación. Puede hacer que la función asigne la memoria y el estado en la documentación que esto sucede y estipule que la persona que llama tiene que liberar la memoria cuando ya no es necesaria (evitando una fuga). Esto significa que la función simplemente puede devolver un char *.

La otra opción es pasar algo de memoria a la función que asignó la persona que llama, y ​​hacer que la función coloque la cadena dentro de esa memoria. En este caso, la persona que llama asigna y es responsable de liberar esa memoria.

Finalmente, mencioné que el tamaño de la memoria y la cadena se deben gestionar cuando se utiliza una cadena mutable. La asignación debe ser lo suficientemente grande para la cadena establecida inicialmente por la función y también para cualquier cambio que se realice después de la función, antes de que se libere la memoria. Si no se hace esto correctamente, puede causar un desbordamiento del búfer al escribir una cadena de caracteres largos para que quepan en la memoria inicialmente asignada; esto es extremadamente peligroso para la salud y la seguridad de su programa. Puede causar errores y agujeros de seguridad que son extremadamente difíciles de detectar (ya que el origen del error, el desbordamiento, puede estar muy alejado de los síntomas que se ven cuando el programa falla).

5

Como el uso anterior de char * está en desuso, ¿no puede simplemente usar una cadena?

const char* func1() {return "string literal";}

string func2() {return "another string literal";}

Ambos funcionan bien, sin advertencias del compilador.

Sin embargo

char* func3() {return "yet another string literal";}

no se compilará en absoluto. Tampoco lo hará

char* func4() {return &"a ref to a string literal?";}

BS dice en "The C++ Programming Language" (Tercera Edición):.

"Una cadena literal se asigna de forma estática por lo que es seguro volver algún partir de una función

const char* error_message (int i)` 
{ 
//... 
return "range error"; 
} 

"La memoria que contiene error en la distancia no va a desaparecer después de una llamada de error_messages().

De modo que cada literal de cadena en un programa se asigna en su propia pequeña porción de memoria que dura mientras dura el programa (es decir, está asignada estáticamente). Poner el const delante del char * le permite al compilador saber que no tiene la intención (y no puede) alterar el pequeño trozo de memoria del literal de cadena que podría ser peligroso, así que dejan que esta asignación se desplace a pesar de la conversión de cadena literal a char * es obsoleto.

Volviendo en cambio a una cadena, debe copiar el literal de cadena en un objeto de tipo cadena, memoria de la que es responsable la persona que llama.

De cualquier forma no hay pérdidas de memoria: cada literal de cadena obtiene su propia pieza de memoria que se limpia al terminar el programa; return to const char * devuelve un puntero a la pieza de memoria de un literal (sabiendo que no puede modificarlo); y volver a una cadena hace una copia en un objeto de cadena existente en el código de la persona que llama que limpia la persona que llama.

Aunque parece un poco desagradable en cuanto a la notación, supongo que dejaron el const char * para mantener la alternativa barata (sin copias).

Cuestiones relacionadas