다른 문자열 리터럴에 대한 두 문자 포인터의 주소가 동일합니다.


80

두 포인터의 값을 인쇄하면 동일한 주소가 인쇄됩니다. 왜?


66
왜 안된다고 생각합니까? 이 두 포인터는 똑같은 것을 가리 킵니다. 여러분이보고있는 것은 아마도 문자열 풀링이라는 최적화 기술의 효과 일 것입니다.
Daniel Kamil Kozar 2013 년

2
데이터는 동일하지만 변수는 다릅니다.
seereddi sekhar 2013 년

2
물론 변수는 다릅니다. p및 의 주소를 취했다면 p1이 두 포인터가 두 개의 개별 주소 아래에 저장되어 있음을 알 수 있습니다. 그들의 가치가 동일하다는 사실은-이 경우-관련이 없습니다.
Daniel Kamil Kozar 2013 년

예, 값을 변경하면 주소가 다릅니다.
seereddi sekhar 2013 년

11
@JanHudec : 질문을 다시 읽으십시오. 이 경우 (컴파일러 최적화로 인해) p == p1(다르지 않음) 그러나 &p != &p1(다릅니다).
MSalters 2013 년

답변:


87

동일한 내용을 가진 두 개의 다른 문자열 리터럴이 동일한 메모리 위치에 배치되는지 또는 다른 메모리 위치에 배치되는지는 구현에 따라 다릅니다.

동일한 주소를 가리킬 수도 있고 가리 키지 않을 수도 있으므로 항상 pp1두 개의 다른 포인터 (컨텐츠가 같더라도)로 취급해야 합니다. 컴파일러 최적화에 의존해서는 안됩니다.

C11 표준, 6.4.5, 문자열 리터럴, 의미 체계

요소에 적절한 값이있는 경우 이러한 배열이 구별되는지 여부는 지정되지 않습니다. 프로그램이 이러한 배열을 수정하려고하면 동작이 정의되지 않습니다.


인쇄 형식은 다음 %p과 같아야합니다 .

이유는 이 답변 을 참조하십시오 .


휘발성을 사용하여 동일한 주소를 사용하더라도 메모리 최적화가 없어야합니다. 한 가지 질문은 내가 포인터 중 하나를 수정하면 다른 포인터의 데이터도 수정된다는 것입니다.
Megharaj 2013 년

8
@Megharaj 포인터i modify one of the pointer, will the data in the other pointed also be modified 는 수정할 수 있지만 문자열 리터럴 은 수정할 수 없습니다. 예를 들어 정의되지 않은 동작을 호출 하는 반면 완벽하게 괜찮습니다 . 이것은 . 사용 여부에 관계없이 여기서 관심있는 동작을 변경해서는 안됩니다. 기본적으로 매번 메모리에서 데이터를 읽도록 강제합니다. char *p="abc"; p="xyz";char *p="abc"; p[0]='x';volatilevolatilevolatile
PP

2
@MSharathHegde 예. 때문에 p문자열 리터럴 포인트 "abc"p[0]='x'시도는 문자열 리터럴의 첫 번째 문자를 수정합니다. 문자열 리터럴을 수정하려는 시도는 C에서 정의되지 않은 동작입니다.
PP

2
@MSharathHegde C 표준에 나와 있기 때문입니다. 그 이유는 주로 사전 표준 C 언어가 문자열 리터럴을 수정할 수 있도록 허용했기 때문에 역사적입니다. 나중에 C 표준 (C89)은 새 코드가이를 수행하지 않고 이전 코드 (사전 표준)가 그대로 작동하도록 정의 하지 않았습니다. 기본적으로 기존 (사전 표준) 코드를 손상시키지 않는 것은 타협이라고 생각합니다. 또 다른 이유는 문자열 리터럴의 유형이 char []C에 있기 때문입니다 . 따라서 읽기 전용 ( const char*C ++의 경우처럼)으로 만들려면 유형 도 변경해야합니다. [계속]
PP

7
부록 C에서 K & R 제 2 판에서 선이있다 : "Strings are no longer modifiable, and so may be placed in read-only memory", 문자열 리터럴이있는 역사적 증거 사용 ;-) 수정로
PP

28

컴파일러는 매우 영리 해 보이며 두 리터럴이 동일하다는 것을 감지합니다. 그리고 리터럴이 일정하기 때문에 컴파일러는 두 번 저장하지 않기로 결정했습니다.

이것이 반드시 그럴 필요는 없다는 것을 언급 할 가치가있는 것 같습니다. 참조하시기 바랍니다 블루 문 '의 이에 대한 대답을 .


Btw : printf()진술은 다음과 같아야합니다.

같은 "%p"포인터 값을 출력하기 위해 사용되어야하며, 이는 타입 포인터 정의 void *만. *1


또한 코드가 return성명을 놓친다고 말하고 싶지만 C 표준이 변경되는 과정에있는 것 같습니다. 다른 사람들은 이것을 친절하게 설명 할 수 있습니다.


* 1 : 포인터에는 void *여기로 캐스팅 할 필요가 없지만 char *다른 모든 유형에 대한 포인터에는 캐스팅 이 필요하지 않습니다 .


감사. 결론은 컴파일러 최적화가 맞습니까? 기본적으로 C에서 주 함수는 0을 반환합니다.
seereddi sekhar 2013 년

@seereddisekhar : 예, 일종의 최적화입니다.
alk

2
@seereddisekhar 그러나 당신이 사용하는 두 개의 문자열 (심지어 포인터)를 비교해야한다는 것을 의미하지 않습니다주의 ==사용해야하는 strcmpy()기능. Alk가 PS : Blue Moon이 방금 추가 한 것처럼 다른 컴파일러가 최적화를 사용하지 않을 수 있기 때문입니다 (컴파일러에 따라 구현이 결정됨).
Grijesh 차우

2
@Megharaj에게 : 이것에 대해 별도의 질문을 제기 해 주시겠습니까? 여기에이 새 질문에 대한 링크를 댓글로 게시 할 수 있습니다.
alk

1
@Megharaj : 문자열 리터럴의 값을 변경할 수 없습니다. 내 질문에서 언급했듯이 그것은 일정합니다.
alk

18

여러분의 컴파일러는 "문자열 풀링"이라는 것을 수행했습니다. 동일한 문자열 리터럴을 가리키는 두 개의 포인터를 원한다고 지정 했으므로 리터럴의 복사본을 하나만 만들었습니다.

기술적으로 : 포인터를 "const"로 만들지 않았다고 불평 했어야합니다.

Visual Studio를 사용 중이거나 -Wall없이 GCC를 사용하고 있기 때문일 수 있습니다.

명시 적으로 메모리에 두 번 저장하려면 다음을 시도하십시오.

여기서는 문자에 대한 두 개의 포인터가 아닌 두 개의 c- 문자열 문자 배열을 원한다고 명시 적으로 설명합니다.

주의 사항 : 문자열 풀링은 컴파일러 / 최적화 기능이며 언어의 일부가 아닙니다. 다른 환경에서 이러한 다른 컴파일러는 최적화 수준, 컴파일러 플래그 및 문자열이 다른 컴파일 단위에 있는지 여부에 따라 다른 동작을 생성합니다.


1
gcc (Debian 4.4.5-8) 4.4.5를 사용하지만 불평하지 않습니다 (경고) -Wall -Wextra -pedantic.
alk

1
예, V4.8.1부터 gcc는 기본적으로 const문자열 리터럴에 사용하지 않는 것에 대해 경고하지 않습니다 . 경고는 옵션에 의해 활성화됩니다 -Wwrite-strings. 다른 옵션 (예 : -Wall, -Wextra또는 -pedantic) 으로는 활성화되지 않은 것 같습니다 .
sleske 2013 년

1
GCC 4.4.7 및 4.7.2 모두 -Wall이 있거나없는 경고를 표시합니다. pastebin.com/1DtYEzUN
kfsone 2013 년

15

다른 사람들이 말했듯이 컴파일러는 동일한 값을 가지고 있음을 인식하고 최종 실행 파일에서 데이터를 공유하도록 결정합니다. 하지만 더 화려 해집니다. 다음과 같이 컴파일하면gcc -O

4195780 4195783나를 위해 인쇄 합니다. 즉, p13 바이트 뒤에 시작 p하므로 GCC는 def( \0종결자를 포함하여 ) 의 공통 접미사를 확인 하고 표시된 것과 유사한 최적화를 수행했습니다.

(댓글이 되기에는 너무 길기 때문에 답변입니다.)


3

코드의 문자열 리터럴은 코드의 읽기 전용 데이터 세그먼트에 저장됩니다. "abc"와 같은 문자열 리터럴을 적을 때 실제로 'const char *'를 반환하고 모든 컴파일러 경고가 있으면 해당 시점에서 캐스팅하고 있음을 알려줍니다. 이 질문에서 지적한 바로 그 이유 때문에 이러한 문자열을 변경할 수 없습니다.


2

문자열 리터럴 ( "abc")을 만들면 문자열 리터럴이 포함 된 메모리에 저장되고 동일한 문자열 리터럴을 참조하는 경우 다시 사용되므로 두 포인터가 동일한 위치를 가리키는 " abc "문자열 리터럴이 저장됩니다.

나는 이것을 얼마 전에 배웠기 때문에 정말 명확하게 설명하지 않았을 수도 있습니다. 죄송합니다.


2

실제로 사용하는 컴파일러에 따라 다릅니다 .

TC ++ 3.5를 사용 하는 내 시스템에서는 두 포인터에 대해 두 개의 다른 값, 즉 두 개의 다른 주소를 인쇄 합니다 .

컴파일러는 메모리에 값있는지 확인 하고 그 존재에 따라 동일한 값이 참조되는 경우 이전에 저장된 값 의 동일한 참조다시 할당 하거나 사용하도록 설계 되었습니다.

따라서 컴파일러 가 코드를 구문 분석 하는 방식에 따라 다르 므로 너무 많이 생각하지 마십시오 .

그게 다야...


1

문자열 "abc"자체가 메모리의 주소이기 때문입니다. "abc"를 다시 쓰면 동일한 주소를 저장합니다.


1

컴파일러 최적화이지만 이식성에 대한 최적화는 잊어 버립니다. 때때로 컴파일 된 코드는 실제 코드보다 더 읽기 쉽습니다.


0

문자열 리터럴을 사용하고 있습니다.

컴파일러가 두 개의 동일한 문자열 리터럴을 포착하면

동일한 메모리 위치를 제공하므로 동일한 포인터 위치를 표시합니다 ./

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.