Delphi String에 관하여..

 

Delphi 2007이하의 String Types 기준
 
3가지 타입이 있음.
  • 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   유니코드. 다중사용자 서버나 다중언어 지원용 어플리케이션에서 사용.
(AnsiString: 때때로 long string 이라 불리며, 많은 목적으로 대부분 이걸 사용함.
WideString: .Net 플랫폼에서 주로 사용함.)  

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은 문자가 문자열에 저장된 수이다.

 

원문: http://blog.naver.com/g2000240023?Redirect=Log&logNo=50021477287

http://www.codexterity.com/delphistrings.htm

 

Long Strings (AnsiString)

long string 이라고도 부르는 AnsiString은 동적으로 할당되는 문자열이다. 최대길이는 메모리가 허락하는 한도까지이다.
 
long string 변수는 4 바이트의 메모리 공간을 차지하는 포인터다.
문자열이 비어있을 때 (=길이가 0 일 때), 그 포인터는 nil 을 가리키며, 추가적으로 사용하는 공간은 없다. 문자열이 비어 있지 않을 때, 그 포인터는 문자열이 들어있는 동적할당된 메모리의 블럭을 가리킨다. 문자열 위치 이전의 8 바이트는 32-bit 길이의 지시자와 32-bit 길이의 레퍼런스 카운트로 이루어져 있다.
이 메모리는 힙 영역에 할당되지만, 이 메모리는 프로그래머가 관리하지 않고 전부 자동적으로 관리된다.
 
왜냐하면 long-string 변수들은 포인터이며, 둘 이상의 이 변수들은 추가적인 메모리의 소비없이 같은 값을 가리킬 수도 있기 때문이다.
컴파일러는 이 메모리를 이용해 자원을 절약하고, 실행속도를 향상시킨다.
long-string 변수가 해제되거나 생성될 때, 레퍼런스 카운트는 감소되거나 증가한다. string 을 가리키고 있던 레퍼런스 카운트가 0 이 되면 그 string 의 메모리는 해제된다.
이 프로세스를 레퍼런스 카운팅이라고 부른다.
인덱싱 과정에서 string 의 문자 하나를 바꿀 경우, 만약 그 레퍼런스 카운트가 1 보다 크다면 그 string 의 복사본이 생성된다.
이것을 가리켜 copy-on-write 라 한다.

원문: http://bloodguy.tistory.com/96 

아래는 델파당에서 검색한 내용을 옮겨보았습니다.

VCL의 String은 reference가 count됩니다. String은 변수는 그냥 포인터 4Byte입니다.
그런데 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

WideString 타입은 동적으로 할당되는 16-bit 유니코드 문자로 이루어진 문자열을 의미하며, Win32 에서, WideString 은 COM BSTR 타입과 호환된다.
 Note: Win32 에서, WideString 은 레퍼런스 카운트를 이용하지 않는다.

 Win32 플랫폼은 싱글바이트, 멀티바이트뿐만 아니라 유니코드를 지원한다.

SBCS(single-byte character set) 에서 각 바이트는 문자열의 한 character이며,                     MBCS(multibyte character set)에서는 일부 character 는 1바이트 또는 그 이상의 바이트를 통해 표현된다.
MBCS에서 가장 첫번째 바이트는 lead byte 라고 불리며,
일반적으로, MBCS에서 하위 128 characters 들은 7-bit ASCII characters 이며, 127 이상의 character 를 결정하는 건 MBCS의 lead byte 이다.
null 값은(#0) 언제나 싱글바이트 문자이다.
 
유니코드 character set 에서 각 문자는 2바이트를 의미하며 유니코드 문자열은 1바이트의 나열이 아니라 2바이트의 나열이다.
Unicode characters 와 strings 는 또한 wide characters 와 wide character strings 로 불린다.
Unicode characters map 의 처음 245은 ANSI character set 이다.
윈도우즈 OS는 유니코드를 지원한다. (UCS-2) 
 
델파이는 Char, PChar, AnsiChar, PAnsiChar, AnsiString 타입을 통해 싱글바이트와 멀티바이트 문자(열)을 지원한다.
(멀티바이트 함수들은 보통 Ansi- 로 시작하는 이름을 가진다. 예를 들어, StrPos 의 멀티바이트 버전 함수는 AnsiStrPos 임)
멀티바이트 문자의 지원은 로케일 별 OS 에 의존적이며,  델파이는 WideChar, PWideChar, WideString 을 통해 유니코드를 지원한다.

원문: 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)

(자세한 사항은 문서 참조)

참조문서: http://www.google.co.kr/url?sa=t&rct=j&q=delphi%20%EB%AC%B8%EC%9E%90%EC%97%B4%20%EA%B5%AC%EC%A1%B0&source=web&cd=1&ved=0CC8QFjAA&url=http%3A%2F%2Fwww.devgear.co.kr%2Fpdf%2Fmarco_cantu_delphi_and_unicode_kor.pdf&ei=Bc17T-6_I8ORiQfGnaCiCQ&usg=AFQjCNE0oq_js4cy2BaiEJ0aVFNAe4BUPg&cad=rjt 

 

Posted by 초코송송이
l