C로 개발을 해오며 겪은 메모리 구조 설계 문제와 해결법

정지효·2025년 4월 24일
0

오늘은 필자가 여지까지 메인 언어로 C를 사용해오며 겪었던 메모리 구조 설계의 문제점과 해결 방법에 대해 글을 작성하고자 한다.


C언어 개발의 첫 번째 난관

C언어로 데이터를 구조하려면 포인터의 함정에 빠지게 된다.
필자가 현재 진행중인 와이어샤크 클론코딩 프로젝트인 미니샤크를 예로 들면 패킷 리스트를 저장하려고 struct PacketNode를 만들어 링크드 리스트로 구성했다, 하지만 next, prev, payload까지 포인트를 사용하다보니 디버깅중 Segmentation fault가 매우 빈번하게 발생하는 문제가 생겨버렸다.
경험해본바 이러한 문제의 원인은 크게 포인터 초기화 누락, 이중 포인터 관리 실수, free 순서 오류 정도가 있었다.
가장 많이 발생하였던 문제가 free 순서 오류인데 이때 메모리 릭이 발생하게 된다. 메모리 릭은 동적으로 할당한 메모리가 할당 해제 상태가 될 수 없게 된 것을 의미한다. 메모리 릭은 생각보다 매우 큰 문제이다. 규모가 작은 프로그램 정도는 괜찮지만 자원이 한정되있는 임베디드 상황이나 규모가 매우 큰 프로그램에서는 메모리를 할당 후 해제하지 않으면 메모리 사용량이 계속 증가하게 되는데 이 상황이 계속 반복되면 결국 시스템 메모리가 부족해 운영체제가 프로그램을 강제 종료 시키거나 메모리 할당에 실패하는 경우가 생긴다.

다음은 메모리 릭의 대표적인 예시이다.

char* a = malloc(30);
char* b = malloc(10);

b = a;  // b가 가지고 있던 주소값을 a에 할당

free(a);  //포인터 a에 할당한 30바이트 메모리 해제
free(b);  // 포인터  b에 할당한 10바이트 메모라가 아닌 a에 할당한 30바이트 메모리 해제

다음과 같은 상황에서는 포인터 b에 할당하였던 메모리 10바이트를 해제할 수 없는 상황이 생기게 되었다. 포인터 b에 10바이트의 메모리를 할당하고 포인터 변수 b에다가 포인터 변수 a에 할당한 메모리의 주소를 할당하였기 때문에 포인터 변수 b는 더이상 10바이트 메모리를 가리키지 않고 포인터 변수 a가 가리키는 20바이트 메모리를 가리기는 상태이다. 따라서 포인터 변수 b에 할당했던 10바이트의 메모리는 해제할 수 없는 상태가 되었다. 이를 메모리 릭(memory leak)이라고 한다. 이러한 메모리 릭이 발생하지 않도록 주의해야한다.


C언어의 꽃 구조체

구조체는 C언어의 꽃이라고도 불린다. 하지만 다른 객체지향 언어에서 클래스에 관련된 문제가 있듯이 C언어에도 구조체에 관한 문제가 있다. 바로 구조체의 분리이다. 구조체 하나에 모든 걸 다 넣으면 개발이 편하지만 구조가 점점 커지고 쿠드 유지보수에 문제가 생긴다. 따라서 책임 기반 분리 라는 것은 적용해야 한다.
이번에는 필자가 진행중이 프로젝트인 미니샤크의 경우로 예시를 들어보겠다.

typedef struct PacketNode{
	titme_t = timestamp;
    char scr_ip[16];
    char dst_ip[16];
    u_char* payload;
    struct PacketNode* next;
} PacketNode;

이런식으로 모든 정보를 구조체 하나에 넣으면 코드 유지보수에 문제가 생긴다. 따라서 다음과 같이 구조체를 분리하는것이 좋다.

typedef struct{
	char src_ip[16];
    char dst_ip[16];
    time_t timestamp;
} PacketMeta;

typedef struct{
	u_char* payload;
    int len;
} PacketData;

typedef struct PacketNode{
	PacketMeta meta;
    PacketData data;
    struct PacketNode* next;
} PacketNode;

이런식으로 구조체를 분리하면 기능별 테스트 작성이 용이하고 일부 구조만 확장이나 변경이 가능하기 떄문에 프로그램의 유지보수성이 증가한다.


내가 경험한 C언어 개발은 단순히 기능을 구현하는 것이 아니라 메모리를 잘 관리하여 안정성과 성능을 확보하는것이 중요했다. 또한 C가 매우 자유로운 언어이니만큼 철저한 관리가 중요했다. 이 글이 필자와 같이 C언어 개발자를 꿈구는 누군가에게 도움이 되었으면 좋겠다는 생각을 하며 이번 글을 마치겠다.

해당 링크는 이번 블로그 글에서 예시를 들기 위해 이용한 프로젝트 깃허브 링크다.
https://github.com/jihyo0331/Minishark

profile
SRIHS infoSec 119th

0개의 댓글