프로그램 실행 시 운영체제에 의해 할당되는 메모리 구조는 다음과 같이 4개 영역으로 구분된다.
코드 영역
text 영역이라고도 하는 코드 영역은 실행할 프로그램의 코드가 저장되는 메모리 공간이다. 저장된 명령어들을 CPU가 읽어와 처리한다.
데이터 영역
data 영역은 전역변수와 static 변수가 할당된다. 프로그램 시작과 동시에 메모리 공간에 할당되어 프로그램 종료 시까지 남아있게 된다.
스택 영역
stack 영역에는 지역변수, 매개변수가 할당된다. 이 영역의 변수들은 지역성을 지닌다. 해당된 지역을 빠져나가면 소멸된다.
힙 영역
heap 영역은 동적인 데이터를 다룬다. 동적인 데이터라는 것은 할당되는 메모리의 크기를 컴파일러가 결정하지 않고, 프로그램 실행 중 호출되는 malloc 함수가 결정한다는 의미이다.
다음 그림을 통해 프로그램 실행에 따른 메모리 할당을 이해할 수 있다.
프로그램을 작성하다 보면 원하는 시기에 생성하고, 소멸할 수있는 변수가 필요할 때가 있다. 전역변수는 프로그램 시작부터 종료까지 유지되고, 지역변수는 한 영역에 진입하고 빠져나올 때까지 유지된다.
필요한 때에 생성하고 소멸되는 변수를 힙 영역에 할당할 수 있다.
malloc 함수를 이용해 힙 영역에 메모리 공간을 할당하고, free 함수로 해제할 수 있다.
#include <stdlib.h>
void * malloc(size_t);
//호출 성공 시 메모리 주소 값, 실패 시 NULL반환
void free(void *ptr);
힙 영역은 프로그래머가 관리하는 메모리 공간이다. malloc 함수호출로 할당된 메모리 공간은 free를 통해 해제하지 않으면 계속 유지된다.
따라서 다음과 같이 할당 후 해제해줘야 한다.
malloc 함수로 메모리를 할당하면 함수는 메모리 주소값을 void형 포인터 형태로 리턴한다. 따라서 다음과 같이 적절히 형 변환을 한 후 사용해야 한다.
이렇듯 malloc과 free 함수를 이용하여 힙 영역에 메모리를 할당하는 것을 동적 할당(dynamic allocation)이라 한다. 할당되는 메모리의 크기를 컴파일러가 결정하지 않고, 프로그램 실행 중 호출되는 malloc 함수가 결정한다.
동적할당을 할 때 블록단위로 할당할 수 있는데, 이 때 calloc함수를 사용하면 된다.
calloc 함수는 다음과 같이 정의되어 있다.
#include <stdlib.h>
void * calloc(size_t elt_count, size_t elt_size);
//호출 성공 시 할당된 메모리 주소값, 실패 시 NULL 반환
이 함수는 elt_size 크기의 블록을 elt_count개 만큼 힙에 할당한다. 할당 방식은 malloc함수와 다르지 않다. 그러나 인자 전달 방식이 다르고 calloc 함수는 할당된 메모리 공간의 모든 비트를 0으로 초기화한다.
2023/05/22
연습문제 풀이 추가
프로그램 사용자로부터 문자열을 입력받아 입력받은 문자열의 단어를 역으로 출력하는 프로그램을 작성하라. 문자열의 입력에 앞서 문자열 최대 길이 정보를 입력받도록 한다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void Reverse(char* string) {
int size = strlen(string);
int e = size - 2, s, i = e; // index start, end
while (i >= 0) {
if (string[i] == ' ') {
s = i + 1;
for (; s <= e; s++)
printf("%c", string[s]);
printf(" ");
e = i - 1;
}
else if (i == 0) {
for( ; i <= e; i++)
printf("%c", string[i]);
break;
}
i--;
}
}
int main(void) {
int string_size;
printf("입력할 문자열의 최대 길이는? : ");
scanf("%d", &string_size);
getchar(); //?
char* ptr = (char*)malloc(string_size + 1);
printf("문자열을 입력하시오 : ");
fgets(ptr, string_size, stdin);
Reverse(ptr);
free(ptr);
return 0;
}
입력할 문자열의 최대 길이는? : 30
문자열을 입력하시오 : I am a boy
boy a am I
이 문제는 단어를 끊어 출력하기 위해 단어의 시작 인덱스인 s, 끝 인덱스인 e를 이용했다. 다만 문자열의 최대 길이를 입력받아 메모리를 동적할당 할 때 계속 정상적으로 이루어지지 않았고 여러 시도 끝에 결국 답안을 참고했다. 답안에서는 getchar() 함수를 붙여 \n
문자를 삭제하도록 했는데, 이 부분이 이해가 가지 않았다. 그런데 실제로 이렇게 해야만 정상적으로 실행됐다.
이는 개행문자를 getchar()로 받아주지 않으면 입력 버퍼에 개행문자 \n
가 남아있기 때문에 다음 입력 때 그 개행문자를 받아버리고 사용자는 입력 기회가 사라진다.
전에 배웠던 기억이 있는데 완전히 잊고 있었다.
프로그램 사용자로부터 정수를 입력받는다. -1을 입력하면 종료되며, 종료 전 입력받은 정수를 순서대로 출력한다. 사용자가 몇개의 정수를 입력할 지 알 수 없으므로 초기 배열 길이는 5이며 배열을 초과하는 경우 길이를 3씩 증가하는 것으로 한다. 배열의 길이를 늘리려면 realloc함수를 이용하면 된다.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int i = 0, num = 0;
int length = 5;
int *arr = (int*)malloc(20);
while (num != -1) {
if (i == length - 1) {
length += 3;
arr = (int*)realloc(arr, length * sizeof(int));
}
printf("정수를 입력하시오(종료 시 -1 입력) : ");
scanf("%d", &num);
arr[i++] = num;
}
for (int j = 0; j < i; j++) {
printf("%d ", arr[j]);
}
free(arr);
return 0;
}
정수를 입력하시오(종료 시 -1 입력) : 2
정수를 입력하시오(종료 시 -1 입력) : 3
정수를 입력하시오(종료 시 -1 입력) : 4
정수를 입력하시오(종료 시 -1 입력) : -1
2 3 4 -1
정수를 입력하시오(종료 시 -1 입력) : 2
정수를 입력하시오(종료 시 -1 입력) : 3
정수를 입력하시오(종료 시 -1 입력) : 4
정수를 입력하시오(종료 시 -1 입력) : 5
정수를 입력하시오(종료 시 -1 입력) : 6
정수를 입력하시오(종료 시 -1 입력) : 9
정수를 입력하시오(종료 시 -1 입력) : 8
정수를 입력하시오(종료 시 -1 입력) : -1
2 3 4 5 6 9 8 -1