VC 계통에서 다음 코드를 보자.
unsigned char ttt[100];
unsigned char *bp = &ttt[1];
unsigned int *bpp = (unsigned int *)bp;
unsigned char 형으로 잡은 포인터를 1 증가 시켜서 unsigned int 형으로 캐스팅 해서 지정하게 하였다.
데이터 덤프를 떠보니 문제가 없었다. 포인터는 정상적으로 사용된다.
ADS와 같은 컴파일러에서 다음 코드를 실험해보자.
unsigned char *eee = (unsigned char *)(PACKET_BUF);
unsigned int *rrr;
eee[0] = 0x11;
eee[1] = 0x22;
eee[2] = 0x33;
eee[3] = 0x44;</P>
eee++;>
rrr = (unsigned int *)eee;
*rrr = 0x55667788;
rrr 포인터는 분명히 PACKET_BUF + 1 번지의 값을 지정하고 있으리라 예상되지만,
메모리덤프를 떠보니 그냥 PACKET_BUF 로 들어갔다.
unsigned int * 포인터는 4바이트 얼라인으로 강제되어 있는 것이다.
아마도 아키텍쳐 + 컴파일러 + 최적화수준 + 컴파일러 옵션의 조합에 따라서 다양한 형태의 버그로 발생할 것이다.
ARM - ADS 환경에서 다음은 모두 같은 결과를 가진다..
unsigned int *aaa = (unsigned int *)0x4000;
unsigned int *aaa = (unsigned int *)0x4001;
unsigned int *aaa = (unsigned int *)0x4002;
unsigned int *aaa = (unsigned int *)0x4003;
아예 에러가 나면 다행인데 에러도 나질 않으니 대마왕 버그가 될 가능성이 있는 부분이 된다.
프로토콜 관련 프로그래밍을 하다보면 패킷 처리를 위해 포인터 연산을 많이 하게 된다.
보통 구조체를 packed 로 눌러서 쓰다가 얼라인이 꼬이는 경우는 흔하다.
특히 구조체 -> void * 형으로 전달 -> 구조체 캐스팅으로 쓰는 경우에는.
참고로 구조체의 바이트를 packed 꽉꽉 누르지 않을 때는, 혼동을 방지하기 위해서 다음과 같이 더미를 넣는 것도 좋다.
typedef struct {
char value_a[2];
char dummy[2];
int value_b;
} aaa;
사실 요즘 IP개념으로 구조적 설계가 된 SoC류 들은 저런 구조체로 레지스터 뭉치를 집어서 쓰는 편인데, 빈 주소를 표시하기 위해서 char reserved[4] 식으로 지정하기도 한다.
이상 끝.