2012-04-24 9 views
7

Es tal vez parece ser una pregunta tonta, pero realmente necesidad de aclarar esto:C++ fundido const, sin saber si esto es seguro

¿Esto traerá ningún peligro para mi programa?

¿Es necesario el const_cast?

Si cambio los valores de los punteros de entrada, ¿funcionará de manera segura con std::string o creará un comportamiento indefinido?

Hasta ahora, la única preocupación es que esto podría afectar a la cadena "some_text" cada vez que modifico el puntero de entrada y lo hace inutilizable.

std::string some_text = "Text with some input"; 

char * input = const_cast<char*>(some_text.c_str()); 

Gracias por darme algunos consejos, me gustaría evitar el rodaje en mi propio pie

+0

Generalmente, dado que 'const_cast' elimina la seguridad, debe evitarlo tanto como sea posible. – Mesop

+0

Gracias por la contribución de todos los cuerpos, ahora entiendo que debo evitar esto para mantenerme seguro –

Respuesta

13

Como un ejemplo de mal comportamiento: la interacción con la implementación Copy on Write de gcc.

#include <string> 
#include <iostream> 

int main() { 
    std::string const original = "Hello, World!"; 
    std::string copy = original; 

    char* c = const_cast<char*>(copy.c_str()); 
    c[0] = 'J'; 

    std::cout << original << "\n"; 
} 

En acción en ideone.

Jello, World!

Problema? Como su nombre lo indica, la implementación de gcc de std::string usa un buffer compartido contado por ref bajo la cubierta. Cuando se modifica una cadena, la implementación verificará cuidadosamente si el búfer se comparte en el momento, y si lo está, lo copia antes de modificarlo, asegurándose de que otras cadenas que comparten este búfer no se vean afectadas por la nueva escritura (de ahí el nombre, Copiar en escrito).

Ahora, con su malvado programa, accede al búfer compartido a través de un método const (prometiendo no modificar nada), ¡pero lo modifica!

Tenga en cuenta que con la implementación de MSVC, que no utiliza Copy On Write, el comportamiento sería diferente ("Hello, World!" se imprimiría correctamente).

Esto es exactamente la esencia de Undefined Behavior.

+16

La esencia de * Undefined Behavior *: Tu mundo se convierte en _jello_. –

+0

Gracias por esta idea –

5

Para modificar un objeto intrínsecamente const por desechar su constness usando const_cast es un comportamiento indefinido .

string::c_str() devuelve un const char *, es decir, un puntero a una cadena de estilo c constante. Técnicamente, modificar esto dará como resultado un comportamiento indefinido.

Tenga en cuenta que el uso de const_cast es cuando tiene un puntero const a datos no const y desea modificar los datos no constantes.

+0

no he encontrado una opción de cadena que realice la operación c_str() sin la const, ¿hay alguna alternativa que prefiera? –

+0

Puede ser el caso que 'c_str' devuelva un puntero a una matriz de' char', y no 'const char'. Entonces, formalmente, lo que puede decir es que no puede garantizar que el programa sea correcto si modifica los objetos a través de 'input', no es garantía de que el programa sea incorrecto. –

+0

@OliverStutz: debe tener otra variable suficientemente asignada (matriz o puntero) para copiar la cadena y luego modificar la cadena copiada a la otra variable. –

1

Sí, traerá peligro, porque

  1. input puntos a lo que sucede c_str estar en este momento, pero si alguna vez some_text cambios o desaparece, se le dejó con un puntero que apunta a la basura . Se garantiza que el valor de c_str será válido solo mientras la cadena no cambie. E incluso, formalmente, solo si no llama al c_str() en otras cadenas también.
  2. ¿Por qué necesita desechar la const? No planea escribir al *input, ¿verdad? Eso es un no-no!
+0

Eso era exactamente lo que quería hacer, modificar la cadena (por ejemplo, eliminar caracteres duplicados) mi mayor problema era que realmente me permitía compilarlo y ejecutarlo, pero eso me hizo abrir esta publicación porque parece que no es lógico lanzar el const de distancia y después de eso soy incapaz de escribirlo y modificarlo correctamente (bueno, ya va hacia el sur pero no es visible para mí) –

+1

@OliverStutz Se pueden eliminar cosas como eliminar caracteres duplicados llamando al built in 'std :: string' funciones. Pero si te gustan más las viejas funciones de C, ve a la vieja C todo el tiempo y haz un 'strcpy' primero. –

2

El std::string gestiona su propia memoria interna, por lo que devuelve un puntero a la memoria directamente como lo hace con la función c_str(). Se asegura de que sea constante para que tu compilador te avise si intentas modificarlo.

El uso de const_cast de esa manera literalmente descarta tal seguridad y es solo una práctica aceptable si está absolutamente seguro de que la memoria no se modificará.

Si no puede garantizar esto, debe copiar la cadena y usar la copia .; sin duda es mucho más seguro hacer esto en cualquier caso (puede usar strcpy).

2

Véase el C++ reference sitio web:

const char* c_str () const; 

"genera una secuencia terminada en nulo de caracteres (c-secuencia) con el mismo contenido que el objeto de cadena y lo devuelve como un puntero a una matriz de caracteres.

Se agrega un carácter nulo de terminación automáticamente.

La matriz devuelta apunta a una ubicación interna con el espacio de almacenamiento requerido para esta secuencia de caracteres más su carácter nulo de terminación, pero los valores de esta matriz no se deben modificar en el programa y solo se garantiza que permanezcan sin cambios hasta la siguiente llamada a una función miembro no constante del objeto de cadena."

4

Simplemente fundición no dará a luz un comportamiento indefinido. la modificación de los datos apuntaban, sin embargo, va. (Also see ISO 14882:98 5.2.7-7).

Si quiere un puntero a datos modificables, puede tener un

std::vector<char> wtf(str.begin(), str.end()); 
char* lol= &wtf[0]; 
+7

wtf & lol - ¿el nuevo foo & bar? – gwiazdorrr

+1

Excepto que ahora, tiene una cadena terminada no nula. que no tiene con 'char * c = str.c_str(); std :: vector foo (c, c + str.size() + 1); ' – stefaanv

1

Esto es algo muy malo de hacer. Vea lo que std :: string :: c_str() does y acuerde conmigo.

En segundo lugar, considere por qué quiere un acceso sin const a las partes internas de std :: string. Aparentemente quiere modificar los contenidos, porque de lo contrario usaría un puntero const char. También le preocupa que no desee cambiar la cadena original. Por qué no escribir

std::string input(some_text); 

Entonces usted tiene un std :: string que pueda meterse con y sin afectar el original, y ha std :: funcionalidad cadena en lugar de tener que trabajar con un C++ prima puntero ...

+0

Si el OP necesita un' char * ', hacerlo así no es bueno, porque tienes exactamente los mismos problemas con el nuevo cadena como con la original! –

1

Otro giro en esto es que hace que el código sea extremadamente difícil de mantener. Un ejemplo: hace unos años tuve que refactorizar un código que contenía funciones largas. El autor había escrito las firmas de función para aceptar los parámetros de const pero luego estaba const_cast ing dentro de la función para eliminar la constness. Esto rompió la garantía implícita dada por la función e hizo muy difícil saber si el parámetro ha cambiado o no dentro del resto del cuerpo del código.

En resumen, si tiene control sobre la cuerda y cree que tendrá que cambiarla, hágalo no const en primer lugar. Si no lo hace, tendrá que tomar una copia y trabajar con eso.

1

es UB. Por ejemplo, se puede hacer algo como esto esto:

size_t const size = (sizeof(int) == 4 ? 1024 : 2048); 
int arr[size]; 

, sin ningún dominante y el comiler no informar de un error. Pero este código es ilegal. La moral es que necesita considerar la acción cada vez.

Cuestiones relacionadas