64비트 Objective-C에 대한 글을 쓰기 앞서서 C 언어 데이터 모델이 무엇인지 이야기하고자 한다. 64비트 시스템이 나오면서 기존의 플랫폼에 따른 타입들의 크기를 재정의 해야할 필요가 생겼는데 이에 대한 이야기이다. 플랫폼에 따라 변하는 기본 타입들은 int
, void*
(pointer), long
, long long
등을 말한다. 물론 16비트 CPU에서 32비트로 전환되던 시절에도 이 이슈는 존재하였다. 예를 들면 16비트 cpu의 경우 16비트의 주소값으로는 65.536Byte 밖에 표현할 수 없는데, 이에 20비트의 포인터를 사용하였었다(모든 기종이 그렇다는 것은 아니다). 여기서 문제는 처리 단위가 16비트이다 보니 segment value(기준 주소를 4비트 시프트)와 offset(segment value로 부터의 실제 좌표의 차이)과 같이 포인터를 좀 더 복잡하게 고민하여 처리해야했다. near pointer와 far pointer로 구분하여 사용하던 시절이 있었다. 어쨋든 32비트에서 64비트로 바뀔 때도 비슷한 이슈가 있었는데, 이 것이 지금 말하려는 C언어 데이터 모델이다.
컴파일러나 플랫폼에 따라 선택한 데이터 모델은 달라진다. 기존 32bit 프로그래밍 모델에서는 ILP32를 사용한다. LP32는 기존 Win16CAPI에서 사용되곤 했다. LLP64의 경우 윈도우에서 채택[2] 했으며, LP64의 경우 일부 유닉스 계열에서 사용한다.
여기서 L은 long
에 해당하며, P는 pointer size를 이야기 하며, I는 int
를 의미, LL은 long long
을 의미한다. long long
의 경우 지원하지 컴파일러에 따라 지원하지 않을 수도 있다[2].
Datatype | LP64 | ILP64 | LLP64 | ILP32 | LP32 |
char | 8 | 8 | 8 | 8 | 8 |
short | 16 | 16 | 16 | 16 | 16 |
int | 32 | 64 | 32 | 32 | 16 |
long | 64 | 64 | 32 | 32 | 32 |
long long | 64 | ||||
pointer | 64 | 64 | 64 | 32 | 32 |
위의 테이블을 보면 각 데이터 모델에 따라서 어떤 값을 가지게 되는지 알 수 있다. pointer의 경우 확실히 다르기 때문에 기존 32bit 코드를 64bit로 변경할 때 꼭 주의 하여야 한다. long type의 변수에 포인터 값을 할당하여 사용하는 경우도 멀티플랫폼을 지원하기위한 개발을 한다거나 할 때 위험하기 때문에 잘 고려해서 선택해야한다.
차라리 C99에서 정확한 사이즈가 지정된 데이터 타입이 추가되었는데, 컴파일러가 C99와 호환된다면 이를 선택하는 것이 좋을 수도 있다. int8_t
, uint8_t
, int32_t
, int64_t
등과 같이 unsigned
된 형태와 8,16,32,64를 지원한다. 하지만 문제 점도 있는데, int32_t
를 썼는데 16비트 컴퓨터에서 동작시킨다면 성능이 나빠질 수도 있다던지 하기 때문에 상황에 알맞게 구현해야할 것이다. 32비트와 64비트 빌드를 둘 다 지원하는 경우도 마찬가지로 데이터 타입의 사이즈를 고려해야 한다. 또한 size_t
인 데이터도 사용할 때 사이즈에 주의해야한다.
참고자료
[1] Unix.org, “64-Bit Programming Models: Why LP64?”, http://www.unix.org/version2/whatsnew/lp64_wp.html
[2] Hook, B. (2005). Write Portable Code: An Introduction to Developing Software for Multiple Platforms. No Starch Press., pp85-89