Posso ver dois 'some'
literais no código assembler gerado pelo MSVC, mas apenas um com clang e gcc. Isso leva a resultados totalmente diferentes de execução de código.
static const char *A = "some";
static const char *B = "some";
void f() {
if (A == B) {
throw "Hello, string merging!";
}
}
Alguém pode explicar a diferença e semelhanças entre essas saídas de compilação? Por que o clang / gcc otimiza algo mesmo quando nenhuma otimização é solicitada? É algum tipo de comportamento indefinido?
Também noto que, se eu alterar as declarações para as mostradas abaixo, clang / gcc / msvc não deixará nenhuma "some"
no código do assembler. Por que o comportamento é diferente?
static const char A[] = "some";
static const char B[] = "some";
c++
language-lawyer
string-literals
string-interning
Eugene Kosov
fonte
fonte
Respostas:
Este não é um comportamento indefinido, mas um comportamento não especificado. Para literais de string ,
Isso significa que o resultado de
A == B
pode sertrue
oufalse
, do qual você não deve depender.Do padrão, [lex.string] / 16 :
fonte
As outras respostas explicaram porque você não pode esperar que os endereços do ponteiro sejam diferentes. No entanto, você pode facilmente reescrever isso de uma forma que garanta
A
eB
não compare igual:A diferença é que
A
eB
agora são matrizes de personagens. Isso significa que eles não são ponteiros e seus endereços precisam ser distintos, assim como os de duas variáveis inteiras deveriam ser. C ++ confunde isso porque faz ponteiros e arrays parecerem intercambiáveis (operator*
eoperator[]
parecem se comportar da mesma forma), mas eles são realmente diferentes. Por exemplo, algo comoconst char *A = "foo"; A++;
é perfeitamente legal, masconst char A[] = "bar"; A++;
não é.Uma maneira de pensar sobre a diferença é
char A[] = "..."
dizer "dê-me um bloco de memória e preencha-o com os caracteres...
seguidos de\0
", enquantochar *A= "..."
diz "dê-me um endereço no qual eu possa encontrar os caracteres...
seguidos de\0
".fonte
*p
ep[0]
não apenas "parecem se comportar da mesma forma", mas por definição são idênticos (desde quep+0 == p
seja uma relação de identidade, pois0
é o elemento neutro na adição de ponteiro-inteiro). Afinal,p[i]
é definido como*(p+i)
. A resposta é bastante válida.typeof(*p)
etypeof(p[0])
são ambos,char
então não há realmente muito que possa ser diferente. Eu concordo que 'parecem se comportar da mesma forma' não é a melhor formulação, porque a semântica é muito diferente. O envio da mensagem me lembrou a melhor maneira de elementos de acesso de matrizes C ++:0[p]
,1[p]
,2[p]
etc. Esta é a forma como os profissionais fazem isso, pelo menos quando querem confundir as pessoas que nasceram após a linguagem de programação C.Se um compilador escolhe ou não usar o mesmo local de string para
A
e issoB
depende da implementação. Formalmente, você pode dizer que o comportamento do seu código não é especificado .Ambas as opções implementam o padrão C ++ corretamente.
fonte
É uma otimização para economizar espaço, geralmente chamada de "string pooling". Aqui estão os documentos para MSVC:
https://msdn.microsoft.com/en-us/library/s0s0asdt.aspx
Portanto, se você adicionar / GF à linha de comando, deverá ver o mesmo comportamento com MSVC.
A propósito, você provavelmente não deve comparar strings por meio de ponteiros como esse, qualquer ferramenta de análise estática decente sinalizará esse código como defeituoso. Você precisa comparar o que eles apontam, não os valores reais do ponteiro.
fonte