델마당에서 퍼온 글.
원본 글 주소 
------------------------------------------------------------------------------
혹시, C, C++ 을 자주 쓰시는 분이 아니신지요? ^^;;
Win32 델파이에서 String 은 AnsiString 이라는 것과
[레퍼런스카운트(4byte)|문자열길이(4byte)|문자열]의 구조를 가진다는 것은 아시리라 봅니다.
그리고 꼭 기억하실 것이 있습니다.
델파이 컴파일러는 이 String 을 아주 지능적(?)으로 관리하고 있다는 겁니다.
즉, 순수하게 C, C++ 의 포인터 관점으로 봐서는 곤란합니다.
Str: String ... 여기서 Str은 4byte 포인터가 맞습니다.
하지만 기억하실 것은 언제든지 최적화를 위해 컴파일러가 이 놈을 좌지우지할 수 있다는 사실입니다.
그것을 벗어나고 싶으시면 배열을 쓰시든지 포인터형을 일관되게 쓰시면 됩니다.
델파이(파스칼)은 그것을 구분하고 있고 또 그러하다는 가정하에 동작하는 것입니다.
즉, 기본 String 은 아주 다이나믹한 놈이라는 사실입니다.
컴파일러의 관점에서 보면 대략 이러하지 않을까 싶습니다.
Str := 'abc';
여기서 Str 은 문자열 'abc' 를 가리키는 포인터이며, 'abc' 의 구조는
[문자열길이(3)|abc] 입니다.
(물론, 제시하신 해당 소스에서 설명입니다.)
마치, 상수형문자열처럼 보는 거지요. 그 다음,
Pc1 := @Str
[1];
이 놈을 만나면서, 컴파일러는 '아하! 좀 가지고 놀아야되는 놈인가봐?' 라고 판단합니다.
이어~ 힙에 메모리를 할당한 다음, 'abc'를 복사한 후에
[레퍼런스카운트(1)|문자열길이(3)|abc] 와 같은 놈을 구성한 후,
해당 메모리의 번지를 Str 에 대입합니다. 즉, 애초에 가리키는 번지에서 변화한 값을 가지게 된 것입니다.
그리고 이 과정을 컴파일러가 자동으로 하는 겁니다.
만약, Temp := Str; 이라는 구문이라도 만나게 되면...
Str 이 가리키는 번지의 값은
[레퍼런스카운트(2)|문자열길이(3)|abc] 로 바뀌게 됩니다.
Str := Str + '삭신만세!! ㅠㅠ'; 와 같은 놈을 만난다면... 새로운 번지에 상기의 문자열을 구성할 수도 있고 안할 수도 있습니다.
즉, 컴파일러 맘이라는 뜻입니다. 만약, 레퍼런스카운트가 2이상 이었다면 1로 바뀌겠지요.
이것은 델파이 컴파일러의 특징이고 C, C++ 컴파일러처럼 동작하지 않는다고 말한다면...'자다가 봉창 두드리는 소리'가 되는 겁니다. ^^
또 한가지 알아두실 부분은...
이렇게 레퍼런스카운트 형태로 String을 관리하다가 함수가 채 끝나기 전에, 해당 String 변수가 더 이상 사용되지 않으면
레퍼런스카운트를 0으로 세팅하고 '지지고 볶든 알아서하세요~'라고 방치하고 또 그렇게 알아서 밀어버리고 재활용합니다.
왜 이렇게 하느냐?
그것은 2G 까지 가질 수 있는 문자열형이기 때문입니다.
즉, 메모리낭비를 막기위한 일종의 가비지컬렉터인거죠.
그렇게 해제된 String 을 어딘가에 포인터로 고이 가지고 있다면, 에러를 뱉든 엉뚱한 값을 출력하든 할 것입니다.
따라서 String 은 String 으로 사용하시되 C, C++ 처럼 쓰고 싶으시다면 애초에 그 용도에 맞게 포인터형 문자열을 선언해서 사용하여야하는 겁니다.
이제, PChar(Str) 과 @Str
[1] 의 차이를 말해야겠군요.
전자는 말그대로 Str 의 포인터를 문자형포인터로 반환하는 것이고,
후자는 델파이가 AnsiString 으로 제대로 처리하게끔 구성한 후(이미 구성되어있다면 별 짓 안할 수도 있습니다. ㅡㅡ)에
해당 문자열의 첫번째 요소의 번지값을 반환하는 것입니다.
즉, 이후의 문자열 연산에 의해 Str 의 포인터값이 바뀔 수 있음을 생각해보면 둘 다 List 에 추가한 후,
두고두고 호출할 성격은 아니라고 보입니다.
이상 부족하지만...
Pc1 := @Str
[1];
Pc := PChar(Str);
했을 때와
Pc := PChar(Str);
Pc1 := @Str
[1];
했을 때 Str 이 담고있는 번지의 값이 달라지는 것의 답과...엉뚱한 문자열이 출력되는 것에 대한 답변이 되지 않았나 싶습니다.
더 이상의 내용은 저도 몰라요~ ㅠㅠ 텨텨~
정영훈 wrote:
>
> var
> pc, pc1: PChar;
> str, ss, ss1: String;
> begin
> str := 'abc';
> //--------------------------------
> pc1 := @str
[1];
> pc := PChar(str);
> //--------------------------------
> ss := Format('%p',
[addr(pc^)]);
> ss1 := Format('%p',
[addr(pc1^)]);
>
> Memo1.Lines.Add(ss);
> Memo1.Lines.Add(ss1);
>
> 코드에서
>
> //---
> //---
> 의 코드 순서를 바꾸는 것에 따라 결과 값(ss, ss1)이 다릅니다.
>
> pc1 := @str
[1];
> pc := PChar(str);
> 위와 같은 순서일 때는 ss, ss1 의 값은 같지만.
>
> pc := PChar(str);
> pc1 := @str
[1];
> 순서로 수행하면 값이 다릅니다.
>
> 이유 설명 좀 부탁드립니다.
>
> 정영훈 wrote:
> > 안녕하세요
> >
> > var str: string;
> >
> > str := 'abc';
> >
> > 에서
> >
> > PChar(str) 와 @str
[1] 이 같다고 생각하고 사용했었는데 좀 다른가 봅니다.
> >
> > var list: TList;
> >
> > str := 'abc';
> > list.Add(PChar(str));
> >
> > 일때
> >
> > ShowMessage(string(list
[0]));
> >
> > 은 문제 없이 'abc' 를 출력하는 반면에
> >
> > var list: TList;
> >
> > str := 'abc';
> > list.Add(@str
[1]);
> >
> > 때는 엉뚱한 문자열을 출력합니다. 즉 다른 번지를 참조하는 듯 한데...
> > 속 시원한 답변 좀 부탁드립니다.
> >
> >
> >
>
Comment