c++ - una - ¿Por qué(solo) algunos compiladores usan la misma dirección para literales de cadena idénticos?
variable string (4)
Puedo ver dos
''some''
literales en el código del ensamblador generado por MSVC, pero solo uno con clang y gcc.
Esto lleva a resultados totalmente diferentes de la ejecución del código.
static const char *A = "some";
static const char *B = "some";
void f() {
if (A == B) {
throw "Hello, string merging!";
}
}
¿Alguien puede explicar la diferencia y las similitudes entre esas salidas de compilación? ¿Por qué clang / gcc optimiza algo incluso cuando no se solicitan optimizaciones? ¿Es este un tipo de comportamiento indefinido?
También me doy cuenta de que si cambio las declaraciones a las que se muestran a continuación, clang / gcc / msvc no deja ningún
"some"
en el código del ensamblador.
¿Por qué el comportamiento es diferente?
static const char A[] = "some";
static const char B[] = "some";
Es una optimización para ahorrar espacio, a menudo llamada "agrupación de cadenas". Aquí está la documentación para MSVC:
https://msdn.microsoft.com/en-us/library/s0s0asdt.aspx
Por lo tanto, si agrega / GF a la línea de comandos, debería ver el mismo comportamiento con MSVC.
Por cierto, probablemente no debería estar comparando cadenas a través de punteros como ese, cualquier herramienta de análisis estático decente marcará ese código como defectuoso. Debe comparar a qué apuntan, no los valores de puntero reales.
Este no es un comportamiento indefinido, sino un comportamiento no especificado. Para los literales de cuerda ,
El compilador tiene permitido, pero no es obligatorio, combinar el almacenamiento para literales de cadena iguales o superpuestos. Eso significa que los literales de cadena idénticos pueden o no compararse igual cuando se comparan con un puntero.
Eso significa que el resultado de
A == B
podría ser
true
o
false
, en el que no debería depender.
De la norma, [lex.string]/16 :
Si todos los literales de cadena son distintos (es decir, se almacenan en objetos que no se superponen) y si no se especifican las evaluaciones sucesivas de un literal de cadena el mismo objeto o un objeto diferente.
Las otras respuestas explicaron por qué no puede esperar que las direcciones de puntero sean diferentes.
Sin embargo, puede volver a escribir esto de una manera que garantice que
A
y
B
no se comparen igual:
static const char A[] = "same";
static const char B[] = "same";// but different
void f() {
if (A == B) {
throw "Hello, string merging!";
}
}
La diferencia es que
A
y
B
ahora son matrices de caracteres.
Esto significa que no son punteros y que sus direcciones deben ser distintas al igual que las de dos variables enteras.
C ++ confunde esto porque hace que los punteros y las matrices parezcan intercambiables (el
operator*
y el
operator[]
parecen comportarse igual), pero son realmente diferentes.
Por ejemplo, algo como
const char *A = "foo"; A++;
const char *A = "foo"; A++;
es perfectamente legal, pero
const char A[] = "bar"; A++;
const char A[] = "bar"; A++;
no es
Una forma de pensar acerca de la diferencia es que
char A[] = "..."
dice "dame un bloque de memoria y llénalo con los caracteres
...
seguido de
/0
", mientras que
char *A= "..."
dice" dame una dirección en la que pueda encontrar los caracteres
...
seguido de
/0
".
Si un compilador elige o no usar la misma ubicación de cadena para
A
y
B
depende de la implementación.
Formalmente puedes decir que el comportamiento de tu código
no
está
especificado
.
Ambas opciones implementan el estándar de C ++ correctamente.