2012-05-13 23 views
8

código de mi compañero se veía así:cadena [longitud()] en C++, ¿está bien?

void copy(std::string const& s, char *d) { 
    for(int i = 0; i <= s.size(); i++, d++) 
    *d = s[i]; 
} 

Sus aplicación se bloquea y creo que es porque este accede a s fuera de rango, ya que la condición debe ir sólo hasta s.size() - 1.

Pero otros muchachos a mi lado dicen que hubo una discusión en el pasado sobre que esto era legal. ¿Puede alguien aclarar esto por mí?

+0

Estaría más preocupado por el puntero de destino. ¿Por qué esa función no toma un parámetro de tamaño? – LaC

+0

Quizás debería intentar copiar 's.c_str()' si quiere tener el terminador nulo en la copia ... – Yaniro

+3

tal vez 'd' tiene una capacidad incorrecta – Abyx

Respuesta

10

Dejemos de lado la posibilidad de que *d no es válido, ya que no tiene nada que ver con lo que la pregunta parece estar dirigido a: si o no std::string operator[]() tiene un comportamiento bien definido cuando se accede al "elemento" en el índice std::string::size().

El estándar de C++ 03 tiene la siguiente descripción de string::operator[]() (21.3.4 "basic_string acceso elemento"):

const_reference operator[](size_type pos) const; 
reference operator[](size_type pos); 

devoluciones: Si pos < size(), devuelve data()[pos]. De lo contrario, si pos == size(), la versión const devuelve charT(). De lo contrario, el comportamiento no está definido.

Desde s en el código de ejemplo es const, el comportamiento está bien definido y s[s.size()] devolverá un carácter nulo.Sin embargo, si s no era const string, el comportamiento sería indefinido.

C++ 11 soluciona este comportamiento extraño de la versión const que se comporta de manera tan diferente a la versión no const en este caso de borde. C++ 11 21.4.5 "basic_string acceso elemento" dice:

const_reference operator[](size_type pos) const; 
reference operator[](size_type pos); 

Requiere: pos <= size().

Devuelve: *(begin() + pos) si es pos < size(), de lo contrario, una referencia a un objeto de tipo T con valor charT(); el valor referenciado no se modificará.

Así que para un compilador de C++ 11, el comportamiento está bien definido si el string es const.

No relacionado con la pregunta, me parece un poco extraño que C++ 11 diga que "el valor al que se hace referencia no se modificará" - no me queda claro si esa cláusula se aplica solo en el caso donde pos == size(). Estoy bastante seguro de que hay una tonelada de código existente que hace cosas como s[i] = some_character; donde s es un no-const std:string y i < s.size(). ¿Es ese comportamiento indefinido ahora? Sospecho que esa cláusula se aplica solo al objeto de caso especial charT().

Otra cosa interesante es que ninguno de los estándares parece requerir que la dirección del objeto devuelto para s[s.size()] esté relacionada de algún modo con la dirección del objeto devuelto para s[s.size() - 1]. En otras palabras, parece que la referencia charT() devuelta no tiene que ser contigua al final de los datos de cadena. Sospecho que esto es para darles a los implementadores la opción de simplemente devolver una referencia a una única copia estática de ese elemento centinela si así lo desean (eso también explicaría la restricción "no se modificará" de C++ 11, asumiendo que se aplica solo al especial caso).

+0

Me parece divertido que 'at', sin embargo, no permita ser llamado con' size() 'como índice. –

+0

gracias por el análisis en profundidad. –

7

cppreference says this:

reference  operator[](size_type pos); 

const_reference operator[](size_type pos) const; 

Si pos==size(),

  • La versión const Devuelve una referencia al personaje con valor de la gráfica() (el carácter nulo). (hasta C++ 11)
  • Ambas versiones devuelven una referencia al carácter con el valor CharT() (el carácter nulo). La modificación del carácter nulo a través de referencias no const resulta en un comportamiento indefinido. (Puesto que C++ 11)

Por lo que es correcto siempre y cuando no modifique el carácter nulo.

+0

El código OP no intenta modificar std :: string, intenta leer de la cadena y ponerla en la matriz char. –

+4

Para que quede claro: si se usa un compilador previo a C++ 11, el acceso a 's [s.size()]' no está definido si 's' no es' const' - ese acceso en el código de la pregunta es correcto solamente porque 's' es' const' (para un compilador anterior a C++ 11). No tiene nada que ver con si se modifica o no el carácter nulo, solo si 's' es' const'. Por supuesto, nada de esto dice nada acerca de la posibilidad de que '* d' pueda ser inválido. –

+1

Me resulta divertido citar un sitio web para demostrar la conformidad estándar, déjame cavar un poco;) –

0

Si desea hacerlo así (el código de su colega intenta copiar la terminación \0 también) puede

  • uso c_str().
  • utilice el lazo con i < s.size() y añada el \0 manualmente después.

Editar:
Bueno, a juzgar por las otras respuestas, me siento más inclinado a pensar ahora que el comentario de Abyx es correcta: la matriz d puede ser desbordante (o que puede incluso no ser asignado). Mira eso primero.
¡Pero asegúrese de copiar también \0!

Cuestiones relacionadas