코딩/C언어

[C언어] 메모리의 동적 할당

poiri3r 2025. 10. 6. 15:55

오늘은 UAF 취약점을 공부하기에 앞서 C언어에서의 동적할당에 대해서 공부해보겠습니다.

 

동적 할당이란 프로그램이 실행중(runtime)에 필요한 만큼의 메모리를 힙 영역에서 빌려 쓰는 것을 말합니다.

동적 할당은 실행 전 크기가 전해지지 않은 데이터를 다룰 때 사용합니다.

정적 할당에 비해 유연성도 뛰어나고, 메모리 효율 또한 좋기 때문에 최적화에도 좋습니다.

 

C언어에서는 mallocfree함수를 통해 메모리를 할당하고, 해제합니다.

#include <stdlib.n>
void *malloc(size_t size); // 힙 영역에서 메모리 할당 
void free(void *ptr);      // 힙 영역에 할당된 메모리 해제

 

malloc함수는 성공  시 할당된 메모리의 주소값을 반환하고 실패시 NULL을 반환합니다.

#include <stdio.h>
#include <stdlib.h>
int main(void) {
    int* ptr1 = (int *)malloc(100); 
    *ptr1 = 123456;
    printf("할당된 주소: %p\n", ptr1);
    printf("%d \n", ptr1[0]);
    free(ptr1);    
    printf("%d \n", ptr1[0]);
    printf("해제 후 포인터: %p\n", (void*)ptr1);
    return 0;
}

이렇게 코드를 적어주고 출력결과를 보겠습니다.

 

할당된 주소의 값과 해제 후 포인터를 보면 주소값이 그대로 남아있는 걸 확인할 수 있습니다.

 

또한 malloc 함수는 할당 실패 시 NULL을 반환하기 떄문에 예외처리 코드를 작성해주면 좋습니다.

int *ptr = (int*)malloc(sizeof(int));
if (ptr == NULL)
    {
       // 예외처리
    }

 

free함수를 사용해서 메모리를 할당해제하지 않으면 메모리 누수가 나기 때문에 사용하고 나서 꼭 free로 할당 해제해주셔야 합니다.

 

 

free함수를 사용한 예제 코드만 한번 작성해보겠습니다.

사용자의 이름을 입력받아 출력하는 함수 char* ReadUsername이 있다고 해보겠습니다.

#include <stdio.h>

char* ReadUsername(void)
{
	char name[30];
	printf("이름을 입력하세요: ");
	scanf_s("%s", name);
	return name;
}  //소멸되는 지역변수의 return은 오류를 발생함

int main(void) {
	char* name1;
	char* name2;
	name1 = ReadUsername();
	printf("name1: %s \n", name1);
	name2 = ReadUsername();
	printf("name2: %s \n", name2);
	return 0;
}

 

위와 같은 코드는 소멸되는 변수의 return으로 스택 메모리를 잘못 반환한 오류입니다.

또 변수를 전역변수로 설정해버리면

#include <stdio.h>
char name [30];

char* ReadUsername(void)
{	
 	printf("이름을 입력하세요: ");
	scanf_s("%s", name);
	return name;
}

int main(void) {
	char* name1;
	char* name2;
	name1 = ReadUsername();
	printf("name1: %s \n", name1);
	name2 = ReadUsername();
	printf("name2: %s \n", name2);
	return 0;
}

 

 

두번째 name2 입력때 첫번째 입력했던 name1의 결과가 바껴서 꼬여버리는 상황이 발생하기 때문에 올바른 코딩이 아닙니다.

올바른 코딩 예제를 보겠습니다.

#include <stdio.h>
#include <stdlib.h>

char* ReadUsername(void) {
    char* name = (char*)malloc(sizeof(char) * 30);
    printf("이름을 입력하세요 : ");
    scanf_s("%s", name, 30);
    return name;
}

int main(void) {
    char* name1;
    char* name2;
    name1 = ReadUsername();
    printf("name1: %s \n", name1);
    name2 = ReadUsername();
    printf("name2: %s \n", name2);
    free(name1);
    free(name2);
    return 0;
}

 

위와같은 코드로 작성하면 정적할당으로 구현하기 힘든 기능들을 효율적으로 구현할 수 있습니다.

 

이번 포스팅에서는 동적할당하는 malloc과 free에 대해 간단하게 알아보았습니다.

다음 포스팅에서는 이러한 동적할당에서 발생하는 보안 위험인 Use After Free에 대해 살펴보겠습니다.

읽어주셔서 감사합니다~