내가 C/C++ 표준을 엄청 강조하는 편인데, 사실 C/C++ 표준 코드라는건 짜기가 굉장히 어려워.


표준에서 데이터 타입의 크기를 정하지 않는다는 사소한 것에서부터, 부호화 정수가 1의 보수인지 2의 보수인지도 정해진 바 없기 때문에 MSB가 1이면 음수라고 판단하는 trick 마저도 사실은 표준 문법에 따라 잘 코딩한다고 해도 원하는 대로 동작하지 않을 수 있어.


이건 특히 signed integral에서 좀 심한데, 사실 표준에서는 INTMAX + 1조차도 undefined behavior이고, unsigned int -> signed int 도 unsigned int의 값이 signed int 범위를 벗어나면 undefined behavior야.


unsigned int a = UINT_MAX;

int b = a; // UNDEFINED BEHAVIOR!!!


컴파일러가 딱 보고 a가 int 표현 범위를 벗어나는구나!! undefined behavior니까 그냥 b = 0으로 세팅해야지!! 라고 해도 표준에 어긋나는 동작이 아니라는거지.


다만 signed int -> unsigned int 동작은 잘 정의되어 있어서 unsigned int a = -1; 에서 a가 unsigned int로 표현할 수 있는 최대값이 되는 것은 보장되어 있어.


거기다 char의 비트수조차도 정해놓지 않기 때문에 하위 바이트들의 값을 얻고자 하는 다음 코드도 사실은 portable한 코드는 아냐.


unsigned int a = something;

unsigned char BYTE_1 = (a & 0xff00) >> 8;

unsigned char BYTE_0 = a & 0xff;


진짜 표준에 맞춰 짜려면 다음처럼 짜야해.


unsigned char BYTE_1 = (a & (((unsigned char)-1) << CHAR_BITS)) >> CHAR_BITS;

unsigned char BYTE_0 = a & ((unsigned char)-1);


또 다른 예를 들자면 포인터의 alignment 관련해서인데, memcpy나 strlen을 구현하다보면 포인터를 long이나 uintptr_t로 형변환해서 alignment를 검사하는 코드들이 많아.


void *ptr;

unsigned long a = (unsigned long)ptr;

if(a % alignof(unsigned long) == 0) {

  // ptr이 align되어있구나!!

}


근데 이 코드는 여러가지 면에서 표준에 맞지 않는 코드야.


일단 1) 포인터의 크기가 unsigned long보다 클 수 있다 는 점과 2) 포인터를 정수로 형변환했을 때 그게 우리가 흔히 생각하는 메모리 주소를 나타낼 거라는 보장이 없다 라는 점이야.


1에 대해선 uintptr_t 쓰면 되지 않느냐할 수 있는데 uintptr_t는 optional typedef라 환경에 따라 uintptr_t가 제공되지 않을 수 있어. sizeof(long long)은 64bit인데 sizeof(void*)가 128bit 인 시스템 정도를 예로 들 수 있겠지. 이런 시스템에선 포인터를 정수로 표현하는 것 자체가 불가능해.


그나마 POSIX를 따른다거나 C++을 사용한다면 posix_memalign이나 std::align을 이용해서 alignment를 확인할 수 있지만 표준 C만으로는 alignment를 확인하는 일조차 불가능해.


결국 프로그래머는 선택을 해야해. 표준에 엄격히 부합하는 코드를 짤 것인가, 아니면 대부분의 환경에서 만족하는 1 byte = 8 bit 같은 가정 하에 코드를 짤 것인가. 이 부분이 사실상 C/C++을 다루는데에 있어서 가장 어려운 부분인 것 같아.


그럼 표준이 무슨 소용이냐 그냥 돌아가게만 짜면 되겠네 할 수도 있는데, 내 생각에 표준이라는 건 사실상 유니콘 같은거야.

현실 세계에선 없지만, 유니콘이 없다 해서 염소를 타는 것보단 말을 타는게 좋겠지.


오늘도 즐거운 하루 되길 바라~