2012-06-21 28 views
5

Al comparar una cadena literal con otra cadena literal con el operador == (o !=), ¿está el resultado bien definido?C++ compare dos literales de cadena

Por ejemplo, ¿se garantiza lo siguiente?

assert("a" == "a"); 
assert("a" != "b"); 

Por favor no digas cosas como "use std :: string" en su lugar. Solo quiero saber este caso específico.

+0

¿Por qué tendrías que hacer esto? Para un falso garantizado, intente 'assert (! 'El mensaje va aquí"); ' – chris

+1

@chris: Curiosidad para uno. También para una idea de implementación de clase enum. –

+0

posible duplicado de [Diferencia de salida en gcc y turbo C] (http://stackoverflow.com/questions/3289354/output-difference-in-gcc-and-turbo-c) – kennytm

Respuesta

15
"a" == "a" 

Esta expresión puede producir true o false; no hay garantías Los dos literales de cadena "a" pueden ocupar el mismo espacio de almacenamiento o pueden existir en dos ubicaciones diferentes en la memoria.

Creo que el lenguaje más cercano en el Estándar C++ es: "Si todos los literales de cadena son distintos (es decir, se almacenan en objetos que no se superponen) se define la implementación" (C++ 11 §2.14.5/12). No hay otros requisitos o restricciones, por lo que el resultado no se especifica.

"a" != "b" 

Esta expresión debe dió false porque no hay manera de que estos dos cadenas literales pueden ocupar el mismo lugar en la memoria: "a"[0] != "b"[0].


Cuando se comparan las cadenas literales de esta manera, en realidad estás comparando los punteros a los elementos iniciales en las matrices.

Debido a que estamos comparando los punteros, las comparaciones relacionales (<, >, <= y >=) son aún más problemática que las comparaciones de igualdad (== y !=) porque sólo un conjunto restringido de comparaciones puntero se puede realizar utilizando el relacional comparaciones Dos punteros solo pueden compararse relacionalmente si son punteros en el mismo conjunto o punteros en el mismo objeto.

Si los dos "a" literales de cadena ocupan la misma posición en la memoria, entonces "a" < "a" sería bien definido y produciría false, debido a que ambos punteros apuntan al elemento inicial ('a') de la misma matriz.

Sin embargo, si las dos cadenas literales "a" ocupan diferentes ubicaciones en la memoria, el resultado de "a" < "a" no está definido, ya que los dos punteros siendo el punto en objetos sin relación alguna comparación.

Dado que "a" y "b" no pueden ocupar la misma ubicación en la memoria, "a" < "b" siempre tiene un comportamiento indefinido. Lo mismo es cierto para los otros operadores de comparación relacional.

Si lo hizo, por alguna razón, desea comparar relacionalmente dos literales de cadena y tener resultados bien definidos, puede utilizar el comparador std::less, que proporciona un ordenamiento estricto-débil sobre todos los punteros. También hay std::greater, std::greater_equal y std::less_equal comparadores.Dado que los literales de cadenas con los mismos contenidos pueden no ser iguales, no sé por qué uno querría hacer esto alguna vez, pero puede hacerlo.

+0

Creo que sería bueno si también pudiéramos debatir sobre comparaciones que no están tan bien definidas, y 'std :: øess' & friends –

+0

@ Cheersandhth.-Alf: Buena idea; adicional. Además, no sé si he mencionado esto, pero me gusta cómo has incorporado "Cheers and hth" en tu nombre para evitar a los contribuyentes más gruñones de Stack Overflow. ;-) –

1

La idea es que en C++ los literales de cadena son matrices. Como las matrices no tienen operadores de comparación definidos para ellas, se comparan utilizando el siguiente mejor ajuste: el operador de comparación de punteros, ya que las matrices decaerán implícitamente en punteros, por lo que cualquier comparación compara la dirección y no el contenido. Como "a" y "b" no pueden estar en la misma ubicación de memoria, "a"! = "B" es una afirmación verdadera. También forma una afirmación estática válida. No se puede hacer ninguna garantía sobre "a" == "a", aunque GCC con -férma-constants (implícita en -O1) puede hacer una probabilidad razonablemente fuerte y -fmerge-all-constants puede darle una garantía (que potencialmente da como resultado un comportamiento no conforme).

Si desea una comparación basada en el contenido, siempre puede usar assert(!strcmp("a", "a")). O bien, puede utilizar algún tipo de strcmp basado constexpr para una afirmación estática:

constexpr bool static_strequal_helper(const char * a, const char * b, unsigned len) { 
    return (len == 0) ? true : ((*a == *b) ? static_strequal_helper(a + 1, b + 1, len - 1) : false); 
} 

template <unsigned N1, unsigned N2> 
constexpr bool static_strequal(const char (&str1)[N1], const char (&str2)[N2]) { 
    return (N1 == N2) ? static_strequal_helper(&(str1[0]), &(str2[0]), N1) : false; 
} 

static_assert(static_strequal("asdf", "asdf"), "no error - strings are equal"); 
static_assert(static_strequal("asdf", "jkl;"), "strings are not equal"); 
assert(!strcmp("asdf", "jkl;")); //no compile error - runtime error 
//cannot use strcmp in static assert as strcmp is not constexpr... 

A continuación, compilar con g ++ -std = C++ 0x (o -std = C++ 11 para gcc> = 4.7) y ...

error: static assertion failed: "strings are not equal" 
Cuestiones relacionadas