강좌 & 팁
버그 없는 코드를 위한 팁 한가지가 생각나서 소개합니다.
스트럭쳐로 구성된 데이터를 받을 때 혹시 이렇게 코드를 작성하시나요?
// sub.htypedef struct { int code; char data[500]; } data_t; // sub.c extern _data_t *get_data( void); // main.c char buff[1024]; int main( void) { memcpy( buff, get_data(), sizeof( receive_data_t)); return 0; }
다른 모듈에 있는 get_data() 함수를 이용하여 data_t 구조의 데이터를 받았습니다.
이를 buff에 복사하려 합니다. 어떤 문제가 있을까요?
현재는 문제가 없습니다만, 다른 분이 data_t의 크기를 변경해서 buff 보다 크게 수정했다면,
실행이 불안해 질 것입니다. 세그멘테이션 에러를 일으키면서 종료될 때가 많죠.
memcpy() 부분을 아래처럼 수정한다면 어떨까요?
memcpy( buff, get_data(), sizeof( buff));
어떻습니까? 나도 모르게 외부 모듈의 내용이 변경되어도 최소한 프로그램이 그냥 죽어 버리는
문제는 피할 수 있습니다. 또한, 구한 값을 확인해서 버그를 쉽게 잡을 수 있구요.
어떤 분은 data_t가 큰 구조이니 이렇게 사용해도 큰 문제가 없지만,
만일 data_t가 몇 바이트뿐이 안 된다면, 예를 들어 3바이트라면 실용적이지 못한 것 아니냐
지적하기도 하지만, 앞으로 항상 작다고 확실하게, 틀림 없이 장담할 수 있다면 모르겠으나,
그러나 그렇다고 하더라도 이는 코딩의 습관입니다. 3바이트 때문에 1024바이트 복사하는 것이
버그로 인해 원인을 파악하느라 고생하는 것보다 훨씬 실용적이지 않을가요?
제일 위험한 코드는 아래 같은 경우입니다.
memcpy( buff, ptr, strlen( ptr));
ptr 문자열을 buff에 복사하는데 ptr 문자열 길이만큼 복사하고 있습니다.
ptr 문자열이 항상 buff보다 작다면 문제가 없겠지만, 만일 어떤 문제로 인해
문자열이 아닌 이미지 데이터를 받았다면 ptr의 길이는 장담할 수 없게 됩니다.
NULL 문자가 buff 크기 보다 안 쪽에 있다면 그나마 다행이지만,
크기를 넘는 쓰레기 값으로 가득하다면 실행 중 종료는 당연합니다.
memcpy()를 사용할 때는 버퍼 크기만큼 복사하는 습관이 버그를 줄이는 습관이라고 생각합니다.