variable una tipos tipo que programacion operaciones manejo expresiones datos constante con carácter caracteres cadenas cadena c++ language-lawyer string-literals string-interning

c++ - una - ¿Por qué(solo) algunos compiladores usan la misma dirección para literales de cadena idénticos?



variable string (4)

https://godbolt.org/z/cyBiWY

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.