일부 컴파일러가 동일한 문자열 리터럴에 동일한 주소를 사용하는 이유는 무엇입니까? https://godbolt.org/z/cyBiWY ‘some’MSVC에서 생성

https://godbolt.org/z/cyBiWY

'some'MSVC에서 생성 한 어셈블러 코드에서 두 개의 리터럴을 볼 수 있지만 clang과 gcc가있는 것은 하나뿐입니다. 이로 인해 코드 실행 결과가 완전히 다릅니다.

static const char *A = "some";
static const char *B = "some";

void f() {
    if (A == B) {
        throw "Hello, string merging!";
    }
}

누구든지 이러한 컴파일 출력의 차이점과 유사점을 설명 할 수 있습니까? 최적화가 요청되지 않았는데도 clang / gcc가 무언가를 최적화하는 이유는 무엇입니까? 이것은 일종의 정의되지 않은 동작입니까?

또한 선언을 아래 표시된 것으로 변경하면 clang / gcc / msvc가 "some"어셈블러 코드에 전혀 남기지 않는다는 것을 알 수 있습니다. 행동이 다른 이유는 무엇입니까?

static const char A[] = "some";
static const char B[] = "some";


답변

이것은 정의되지 않은 동작이 아니라 지정되지 않은 동작입니다. 대한 문자열 리터럴 ,

컴파일러는 동일하거나 겹치는 문자열 리터럴에 대한 저장소를 결합 할 수 있지만 필수는 아닙니다. 즉, 포인터로 비교할 때 동일한 문자열 리터럴이 동일하게 비교할 수도 있고 그렇지 않을 수도 있습니다.

즉,의 결과 A == Btrue또는 false일 수 있으며 의존해서는 안됩니다.

표준에서 [lex.string] / 16 :

모든 문자열 리터럴이 구별되는지 (즉, 겹치지 않는 객체에 저장 됨), 문자열 리터럴의 연속적인 평가가 동일한 객체 또는 다른 객체를 산출하는지 여부는 지정되지 않습니다.


답변

다른 답변은 포인터 주소가 다를 것으로 예상 할 수없는 이유를 설명했습니다. 그러나 당신은 쉽게 보장하는 방식으로이 문제를 다시 작성할 수 AB그렇지 않은 동일 비교 :

static const char A[] = "same";
static const char B[] = "same";// but different

void f() {
    if (A == B) {
        throw "Hello, string merging!";
    }
}

차이점은 존재 AB지금은 문자의 배열입니다. 이것은 그들이 포인터가 아니며 두 정수 변수의 주소와 마찬가지로 주소가 구별되어야 함을 의미합니다. 이 포인터와 배열은 교환 보인다 (수 있기 때문에 C ++이 혼란 operator*operator[]동일하게 동작하는 것),하지만 그들은 정말 다릅니다. 예를 들어 뭔가 const char *A = "foo"; A++;완벽하게 합법적이지만 const char A[] = "bar"; A++;그렇지 않습니다.

차이점에 대해 생각하는 한 가지 방법 char A[] = "..."은 “나에게 메모리 블록을 제공하고 ...뒤에 오는 문자로 채우십시오 \0char *A= "..."라고 말하는 반면 ” ...뒤에 오는 문자를 찾을 수있는 주소를 제공하십시오”라고 말하는 것 \0입니다.


답변

여부 컴파일러이 선택하는가에 대해 동일한 문자열의 위치를 사용 A하고 B구현까지입니다. 공식적으로 코드의 동작이 지정되지 않았다고 말할 수 있습니다 .

두 선택 모두 C ++ 표준을 올바르게 구현합니다.


답변

이것은 종종 “문자열 풀링”이라고하는 공간 절약을위한 최적화입니다. 다음은 MSVC에 대한 문서입니다.

https://msdn.microsoft.com/en-us/library/s0s0asdt.aspx

따라서 명령 줄에 / GF를 추가하면 MSVC에서 동일한 동작을 볼 수 있습니다.

그건 그렇고 당신은 아마도 그런 포인터를 통해 문자열을 비교해서는 안되며, 괜찮은 정적 분석 도구는 해당 코드를 결함으로 표시합니다. 실제 포인터 값이 아니라 그들이 가리키는 것을 비교해야합니다.