Delphi String에 관하여..
- Short strings.
- Long strings.
- Wide (Unicode) strings.
Type | Maximum Length | Memory required | Used for |
ShortString | 255 characters | 2 to 256 bytes | 이전 버전과의 호환을 위해 존재함. |
AnsiString | ~ 2^31 characters | 4 bytes to 2GB | 8-bit (ANSI) characters, DBCS ANSI, MBCS ANSI, etc. |
WideString | ~ 2^30 characters | 4 bytes to 2GB | 유니코드. 다중사용자 서버나 다중언어 지원용 어플리케이션에서 사용. |
Short String
델파이의 조상격인 Turbo-Pascal에서는 단지 한종류의 String 타입밖에 없었다. 바로 ShortString 타입이다. 터보파스칼 시절 ShortString은 디폴트 String 타입이었고, 우리가 단순하게 String으로 선언을 하면 기본적으로 ShortString이 선언이 되었다. ShortString은 기본적으로 힙에 생성이 되지 않고, 스택에 생성이 된다. 메모리 할당 관점에서 보면 ShortString은 다른 Integer, Boolean, Record, Enum타입등과 달리 정적할당 타입이다. 정적할당이다 보니 ShortString은 당연히 런타임시 사이즈가 변할수 없다. 그래서 ConCat(문자열 붙이는 함수), Insert(문자열에 추가하는 함수)등의 함수를 쓸 때 문제를 야기시켰다. 이 문제를 회피하기 위해, 터보파스칼(델파이)은 모든 String 인스턴스에 256바이트의 최대 사이즈를 미리 할당하였다. ShortString의 첫번째 요소는 String의 실제 길이를 나타내므로 ShortString은 최대 255 글자를 가질수 있다.
var S: ShortString;
실제로 위와 같이 선언하면 스택에는 256 바이트가 사용된다. ShortString 변수가 지역변수이거나, 전역변수이거나 레코드나 클래스에 사용된다 해도 똑같이 256바이트가 사용된다.
ShortString의 메모리 구조는 아래와 같다. n은 문자가 문자열에 저장된 수이다.
[출처] 델파이에서의 String 타입(번역중..)|작성자 독사
원문: http://blog.naver.com/g2000240023?Redirect=Log&logNo=50021477287
http://www.codexterity.com/delphistrings.htm
Long Strings (AnsiString)
아래는 델파당에서 검색한 내용을 옮겨보았습니다.
그런데 4Byte에 적힌 address위치에 가 보면 실제data가 있죠.
procedure TForm1.Button1Click(Sender: TObject);
var
s: AnsiString;
begin
s:='abcd';
ShowMessage(IntToStr(sizeof(s))); //'4'
ShowMessage(IntToStr(sizeof(AnsiString))); //'4'
end;
위 코드에서 s의 size나 AnsiString의 크기는 4입니다.
즉 String변수는 포인터입니다.
실제 Data가 기록된 위치의 메모리 주소(Address)가 적힌 것입니다.
[AnsiStirng의 실제 기록 구조]
AllocSize = ( StrRec+ 실제 Data ) 까지의 크기가 기록되어있습니다.
RefCount = 말그대로 reference-Count 로 AnsiString변수가 참조하고 있는 숫자입니다.
Length = 실제Data의 길이입니다.
String의 길이를 구할때..
Delphi(VCL)의 AnsiString이 Lenth함수가 c에서 strlen보다 속도가 빠른데..
AnsiStirng의 Length를 구하면 위에 StrRec구조체에서 Length값을 그냥 return하지만
c에서 strlen은 $0 즉 NULL문자가 나타날때까지 처음부터 계속 search합니다.
그래서 AnsiString의 Length가 c의 strlen보다 속도를 비교할수 없을 정도로 빠르죠
참조 http://cafe.naver.com/bcbmaster/850
var
s1,12: Stirng;
begin // 1)
s1:='abcd'; // 2)
s2:=s1; // 3)
s2[1]='k'; // 4)
end; // 5)
위 소스에서 1) ~ 4) 과정을 설명해드리면...
1) AnsiString을 선언하면 스택에 4Byte 씩 2개 변수가 할당됩니다.
문론 내용은 nil 아무것도 없습니다.
2) heap(힙)에 strrec(12Byte)포함하여 실제 data 4Byte까지 총 16Byte가 할당됩니다.
AllocSize = 16
RefCnt = 1
Length = 4
data = 'abcd'
위와같이 메모리에 기록되고 stack에 s1위치에는 Data위치의 address가 기록됩니다.
3) s2:=s1 하면
RefCnt = 2 로 바뀌고
S2변수가 있는 stack위치에 Data가 s1과 똑같은 값 Data위치의 addr이 기록됩니다.
4) s2의 값의 일부를 변경하면
* s1의 가리키던 StrRec의 RefCnt =1 로 바뀝니다.
* 그리고 다시 또다른 힙에 14+8=22Byte의 메모리가 확보되고 다음과 같이 내용이 기록됩니다.
AllocSize = 22
RefCnt = 1
Length = 4
data = 'kbcd' //실데이타 5Byte인데.. 4의 배수로 할당되어서 8Byte가 할당됨
* 그리고 s2의 stack위치값은 새로할당된 data의 힙address가 기록됩니다.
음.. 그리고 AllocSize가 왜 12+data_length가 아니라 14+data_length인지는 저도 정확한 이유를 잘 모르겠네요
...
5) 함수를 빠져나가면 stack에 할당된 s1 , s2 가 삭제되겠죠
string변수가 삭제 될때는 RefCnt가 하나씩 줄어듭니다.
만약 RefCnt가 0이 될때면 메모리에 할당된 StrRec부터 Data까지 모두 free됩니다.
만약 전역변수에 문자열을 할당하면 전역변수의 내용을 바꾸지 않은한 RefCnt는 프로그램이 종료할때까지 항상 1보다 크겠죠
원문: http://www.delmadang.com/community/bbs_view.asp?bbsNo=17&bbsCat=41&indx=417354
WideString
Win32 플랫폼은 싱글바이트, 멀티바이트뿐만 아니라 유니코드를 지원한다.
원문: http://bloodguy.tistory.com/96
Delphi 2009 이상
상당히 오랫동안 Delphi에는 문자를 나타내기 위해 다음의 두 가지 개별적인 문자 타입들을 가지고 있었습니다.
AnsiChar: 8비트 표현을 사용 (256개의 기호들을 표시), 코드 페이지에 따라 다르게 해석됨
WideChar: 16비트 표현을 사용 (64K의 기호 표시)
이 측면에서 Delphi 2009에 변화된 점은 없습니다. 달라진 점은, AnsiChar의 별칭이었던 Char 타입이 이제 WideChar의 별칭이 되었다는 것입니다. 컴파일러가 코드에서 Char를 발견할 때마다 WideChar로 해석합니다. 이런 새 컴파일러 디폴트를 변경할 수 있는 방법은 없습니다.이는 많은 소스 코드에 영향을 미치고 많은 결과를 양산하는 상당히 큰 변화입니다. 예를 들어 PChar 포인터는 이제 이전의 PAnsiChar가 아닌 PWideChar의 별칭입니다.
문자열과 UNICODESTRING
Char 타입 정의에서의 변경 사항은 문자열 타입 정의에서의 변경 사항과 밀접한 관련이 있기 때문에 매우 중요합니다. 문자와는 다르게 문자열은 이전에는 없었던 새로운 데이터 타입(UnicodeString이라고 함)에 매핑됩니다. 내부 구조도 AnsiString 타입의 표현과는 상당히 다릅니다.
WideChar 타입에 기반한 문자열을 나타내는 WideString 타입이 언어에 이미 있는데, 왜 힘들게 새 데이터 타입을 정의해야 할까요? WideString은 참조가 카운트되지 않으며 성능과 유연성의 측면에서 매우 취약합니다. 예를 들어 WideString은 네이티브 FastMM4가 아닌 Windows 글로벌 메모리 할당자(allocator)를 사용합니다.
AnsiString과 마찬가지로 UnicodeString은 참조가 카운트되며 copy-on-write 의미를 사용하고 성능이 우수합니다. AnsiString과 달리 UnicodeString은 문자별로 2바이트를 사용하며 UTF-16을 기반으로 합니다. 사실상 UTF-16은 변수 길이 인코딩이며, 때때로 UnicodeString은 두 개의 WideChar 서로게이트 요소(즉, 4바이트)를 사용하여 단일 유니코드 코드 포인트를 나타냅니다.
문자열 타입은 이제 Char 타입에서와 마찬가지로 하드 코딩된 방식으로 UnicodeString에 매핑됩니다. 이를 변경할 수 있는 컴파일러 지시어나 다른 방법은 없습니다. 계속해서 AnsiString 문자열 타입을 사용해야 하는 코드가 있으면 해당 코드를 AnsiString 타입의 명확한 선언으로 교체해야 합니다.
문자열의 내부 구조
새 UnicodeString 타입과 관련된 주요 변경 사항 중 하나는 내부 구조입니다. 새로운 구조는 참조 카운트되는 문자열 타입 모두(UnicodeString 및 AnsiString)에 해당되며, 참조 카운트되지 않는 문자열 타입들(ShortString 및 WideString)에는 해당되지 않습니다.
기존의 AnsiString 타입의 구조는 다음과 같았습니다.
-8 | -4 | 문자열 참조 주소 |
참조 카운트 | 길이 | 문자열의 첫 번째 문자 |
첫 번째 요소(문자열 자체의 시작 부분부터 역으로 카운트)는 Pascal 문자열 길이이며 두 번째 요소는 참조 카운트입니다.
Delphi 2009에서는, 참조 카운트된 문자열의 구조는 다음과 같습니다.
-12 |
-10 |
-8 |
-4 |
문자열 참조 주소 |
코드 페이지 |
요소 크기 |
참조 카운트 |
길이 |
문자열의 첫 번째 문자 |
길이와 참조 카운트 외에, 새로운 필드는 요소 크기와 코드 페이지를 나타냅니다. 요소 크기는 AnsiString과 UnicodeString 사이에 식별하는 데 사용되며, 코드 페이지의 경우는 UnicodeString 타입이 고정 코드 페이지 1200을 가지므로 AnsiString 타입에서만 의미를 갖습니다.
해당 지원 데이터 구조는 다음과 같은 System 유닛의 임플먼테이션 섹션에서 선언되어 있습니다.
type
PStrRec = ^StrRec;
StrRec = packed record
codePage: Word;
elemSize: Word;
refCnt: Longint;
length: Longint;
end;
UnicodeString에 의해 리턴되는 코드 페이지는 1200이며, 이것은 글로벌 변수인 DefaultUnicodeCodePage에 저장되어 있는 숫자 값을 리턴한 것입니다. 위의 코드와 결과에서 Length는 문자 수를 리턴하므로 문자열 길이를 바이트 단위로 확인할 수 있는 직접적인 호출은 없습니다.
물론 다음 표현식을 사용하여, 길이 값에 각 문자의 바이트 크기를 곱하여 문자열 길이의 바이트 값을 알 수 있습니다.
Length(str1) * StringElementSize(str1)
(자세한 사항은 문서 참조)