구조체 열거형 ENUM
구조체 열거형 enum을 사용한 코드를 보게 되어 공부하고자 한다.
다음에는 코드에 구조체를 사용할 시 열거형을 활용해봐야겠다. 구조체 변수명으로만 하다보니 길어지고 가독성이 떨어졌던거 같다.
1. 열거형 (enum)
- 이름을 갖는 정수형 상수를 열거하여 놓은 사용자 정의 자료형
- 코드의 가독성 및 프로그래멍의 편리성을 향상 시킴
[형식]
enum 열거형명
{
열거자 1 = 값1,
열거자 2,
열거자 3 = 값3
};
- enum으로 정의된 각 상수들의 초기 값은 별도로 지정하지 않으면 기본적으로 1씩 순차적으로 증가한 값으로 정ㅇ의된다.
- 프로그래머는 열거자의 값을 임의로 설정할 수 있다.
ex.)
enum rainbow
{
red = 1,
orange,
yellow = 100,
green,
blue,
purple = 200
};
=> orange = 2, green = 101, blue = 102로 정의 되어 진다.
*** 열거형 변수는 열거형에 정의된 상수만을 가질 수 있음
예제
#include <stdio.h>
enum season {
spring=1,
summer,
fall,
winter
};
int main()
{
int a;
while (1)
{
printf("1.spring, 2.summer, 3.fall, 4.winter : ");
scanf("%d", &a);
switch (a)
{
case spring:
printf("봄\n"); break;
case summer:
printf("여름\n"); break;
case fall:
printf("가을\n"); break;
case winter:
printf("겨울\n"); break;
}
}
return 0;
}

- 열거형을 이용하여 프로젝트에서 아이템 구매 시 구조체 배열 보다 열거형을 사용하였다면 함수 매개변수길이가 짧아졌을 거 같다
- 다음에는 로직을 짤때 가독성을 생각하여 짜야겠다.
동적 메모리 할당 (MALLOC)
**프로그램이 실행되기 위해서는 메모리가 필요하다. 컴파일러는 컴파일 시점에 소스 코드를 읽고, 변수 타입들의 크기에 따라 메모리를 할당한다.
이처럼 프로그램이 실행되기 전, 컴파일 시점에 소스 코드를 읽고 메모리 공간을 확보하는 것을 정적 할당(static allocation) 이라고 한다.
그렇다면 동적 할당(dynamic allocation)이란 무엇일까?
컴파일 타임이 아닌 프로그램이 실행되는 중인 런타임에 필요한 만큼의 메모리 공간을 확보하는 것을 의미한다.
동적 할당이 필요한 이유?
그때 그때 필요할때마다 새로운 메모리 공간을 할당하는 것이 아니라, 컴파일 타임에 미리 넉넉한 메모리 공간을 확보해두면 되지 않을까? 라는 생각을 해볼 수 있다. 물론 가능은 하다. 그러나 메모리란 무한한 자원이 아니며, 한정 되어 있다. 만약 우리가 100000000 byte 사이즈의 메모리를 할당해두고 실제로는 10 byte만 사용한다면, 남은 메모리 공간을 비효율적으로 사용하게 된다.
동적할당이 필요한 이유는 그때그때 필요한 만큼만 메모리 공간을 확보하고, 다 사용했다면 free 시켜 줌으로써 메모리 공간을 해제함으로서 한정된 메모리 공간을 효율적으로 사용할 수 있게 되는것이다.
이는 함수가 종료되거나 변수의 영역을 벗어나면 자동으로 메모리 해제가 이루어지는 스택과는 다르다.
동적 할당은 스택이 아닌 어디에서 메모리 할당과 해제가 일어날까?
[힙(heap) 영역]
힙(heap) 이란 C언어나 자바와 같은 프로그래밍 환경에서 원시 자료형(primitive type)이 아닌 보다 큰 크기의 데이터를 담고자 동적으로 할당하는 메모리 공간을 지칭한다.
정리해보자면 c언어에서 말하는 동적 할당(dynamic allocation)이란 힙(heap) 영역에 필요할 때마다 메모리 공간을 할당하고, 더 이상 필요하지 않을 경우 메모리를 해제해주는 과정을 의미한다. 이를 통해서 다음과 같은 장점을 취할 수 있다.
[장점]
- 상황에 따라 원하는 크기 만큼의 메모리가 할당되므로 경제적이다.(malloc or calloc)
- 이미 할당된 메모리라도 언제든 크기를 조정할 수 있다(realloc)
[단점]
- c언어의 경우 GC(Garbage Collector)가 없기 때문에, 개발자가 직접 명시적으로 메모리를 해제해주어야한다.(free)
- 만약 이를 하지 않았을 경우, 메모리 누수가 나타나고 이는 디버깅 하기 매우 까다롭다.
malloc 또는 calloc을 호출하게 되면 힙 영역에 필요한 만큼의 메모리 공간을 확보하고 이후 반환 타입으로 해당 메모리 공간의 시작 위치를 포인터로 반환한다. 또한 할당된 메모리를 어떤 목적에 사용할지 함수에서 판단하기 어렵기 때문에, return 타입은 void * 형을 return 하며, 반환 받는 쪽에서 타입 캐스팅을 통해 사용해야한다.
[malloc 과 calloc 의 차이]
[선언방식]
int *arr=(int *)malloc(10*sizeof(int));
int *arr=(int *)calloc(10,sizeof(int));
malloc은 매개변수로 입력한 크기 만큼을 그대로 할당하는것이고, calloc은 두번째 매개변수의 크기를 첫번째 매개변수 갯수 만큼을 할당해 달라는 식으로 요청한다. 위 두 명령어 모두 똑같은 크기의 공간을 확보한다.
두 명령어의 차이는 초기화 값에 그 차이가 있다.
malloc의 경우에는 메모리 공간만 할당하므로, 초기화되지 않은 메모리 공간에는 쓰레기 값들이 들어있다. 그러나 calloc 같은 경우에는 할당 후 해당 메모리 공간을 0으로 초기화한다.
따라서 성능 자체는 초기화를 시키지 않는 malloc이 조금 더 낫다. 그러나 상황에 따라서 초기화가 필요할 경우는 calloc을 선택하는것도 좋은 방법이 될 것 같다.
[메모리 해제]
앞서서 확인한 메모리 할당 명령어인 malloc 또는 calloc을 이용해서 메모리 공간을 할당하였고, 원하는 동작들을 모두 수행했다면 할당한 메모리 공간을 해제해주어야 한다.
처음 말했던 것처럼 우리의 메모리 공간은 한정적이며, 더 이상 사용되지 않을 메모리 공간이라면 해당 메모리 공간을 할당 해제 해줌으로써 다른 프로그램이 해당 공간을 재활용 할 수 있게 된다.
c언어에서는 free 함수를 이용해 특정 주소의 메모리 공간을 할당 해제 해줄 수 있다.
[메모리 누수란?]
"메모리 누수란 동적으로 할당한 메모리가 할당 해제(free) 될 수 없는 상태가 된 것을 의미한다."
결국 위와 같은 상황들이 반복되면 동적으로 할당된 메모리가 해제되지 못하고 계속 남아있게 되기 때문에, 결국 시스템의 메모리가 부족해져 운영체제가 메모리 할당에 실패하여 프로그램을 종료시킨다.
메모리 누수의 간단한 예시는 아래와 같다.
int *a = malloc(5); // 5byte 메모리 공간 선언
int *b = malloc(10); // 10byte 메모리 공간 선언
b = a; // b가 a의 주소를 가리킴
free(a); // a할당 해제
free(b); // b도 a의 주소를 가리키므로, a할당 해제
위와 같은 상황이라면, b는 결국 힙 영역에 해제되지 않고 계속 남아있게 된다. 이러한 상황을 바로 메모리 누수(Memory leak)라고 한다.
이를 방지하기 위해서는 malloc 또는 calloc을 사용했다면, 의식적으로 free를 사용하는 습관을 들여야 하며, 동적 메모리할당을 한 포인터의 주소를 직접 바꾸는 코드는 작성하지 않는 것이 좋다.
[메모리 재할당]
"이미 할당되어 있는 메모리 공간의 크기를 변경하여 재할당 하는 것을 의미한다."
재할당은 realloc 함수를 이용해 메모리 공간 재할당이 가능하다. realloc의 함수 시그니처는 아래와 같다.
void *realloc(void *ptr, size_t size);
=> 이를 해석해보면 "ptr 위치에 해당하는 메모리 공간의 블록의 크기를 size로 조절한다는 의미가 된다."
<ptr == NULL일 경우>
재할당을 위한 메모리 공간이 선언되어 있지 않는 것이므로, malloc 또는 calloc을 이용해 새로운 메모리 공간을 할당하는것과 동일한 기능을 수행하게 된다.
<size == 0일 경우>
현재 ptr 위치의 메모리 블록 크기를 0으로 만든다는 의미이므로, 이는 곳 해당 메모리 공간을 할당 해제 한다는 의미와 같다. 따라서 이는 free 함수를 호출하는 것과 동일한 기능을 수행하게 된다.
<ptr != NULL일 경우>
현재 ptr 위치에 할당되어 있는 메모리가 있고, 이 메모리 블록의 크기를 size로 변경하기 위한 동작을 수행한다.
기존에 ptr 다음 블록이 이어서 위치한 상태라면, ptr이 해당 위치에서 메모리 공간의 크기를 늘리는것은 어렵기 때문에, 어쩔 수 없이 기존 ptr 블록의 내용을 유지한 채로 확장된 메모리 블록을 새로운 주소에 할당받게 된다.
재할당은 일반적으로 기존 블록 크기보다 크게 할당되는것이 보편적이긴 하다. 그러나 반대로 기존 블록보다 작게 할당되는 경우도 가능하다. 만약 기존에 8byte 크기만큼의 블록을 4byte 만큼으로 축소시키려고 한다면, 재할당하는 4byte블록의 내용은 기존 8byte 블록의 4byte까지의 내용으로 채워지게 된다.
동적 메모리 할당 예제
#include <stdio.h>
#include <stdlib.h>
//동적 메모리 할당 구현하기
int main()
{
int* pi;
pi = (int *)malloc(sizeof(int)); //malloc -> 동적 메모리 할당
if(pi == NULL) //동적 메모리 할당에 실패하면 NULL을 반납함
{
printf("동적 메모리 할당이 실패 하였습니다.");
exit(1);
}
*pi = 100;
printf("%d\n",*pi);
free(pi); //반드시 FREE 를 사용하여 동적 메모리 할당 헤제
return 0;
}
예제2) 소문자 a~z 를 동적할당을 이용하여 대입하기.
#include <stdio.h>
#include <stdlib.h>
//동적 메모리 할당 danamic memory allocation 예제 2)
//소문자 a~z 를 동적할당을 이용하여 대입하기
int main()
{
char* pc = NULL; //pointer char 변수 초기화
int i;
pc = (char*)malloc(sizeof(char) * 100); // 동적 메모리공간 1byte 크기 100개 할당
if(pc == NULL) // 동적 메모리 할당이 실패하면 NULL 을 반납함
{
printf("동적 메모리 할당 실패");
}
for(i=0;i<26;i++)
{
*(pc+i) = 'a' + i; //자동 형 변환을 이용하여 'a' 의 값 == 97 을 i의 값 만큼 증가시킴 a~z대입
}
*(pc+i) = NULL; // %s포맷의 끝을알려주기 위한 과정
printf("%s",pc);
free(pc); //동적 메모리 할당 해제
}

예제3) 정수 대입하기
#include <stdio.h>
#include <stdlib.h>
//동적 메모리 할당 danamic memory allocation 예제 3)
//정수 5개 처리하기
int main()
{
int i,num;
int* pi;
pi = (int*)malloc(sizeof(int) * 5); //4byte 공간 5개 동적 메모리 할당.
if(pi == NULL) //동적 할당이 실패하면 NULL을 반납함
{
printf("동적 할당 실패");
}
for(i=0;i<5;i++) //pi가 가르키는 값에 i를 대입
{
*(pi+i) = i;
}
for(i=0;i<5;i++)
{
printf("[%d]",*pi+i); //pi 가 가르키는 '값' 출력 ***포인터 표기법
}
printf("\n");
for(i=0;i<5;i++)
{
printf("[%d]",pi[i]); //pi[i] 인덱스로 접근하여 출력 ***배열 표기법
}
free(pi); //동적 할당 해제
}

'LAB > C' 카테고리의 다른 글
C언어 파일 입출력 (0) | 2024.09.01 |
---|---|
[24.08.13] c언어의 기초 11 (0) | 2024.08.13 |
[24.08.12] c언어의 기초10 (1) | 2024.08.12 |
[24.08.11] c언어의 기초 9 (1) | 2024.08.11 |
[24.08.10] c언어의 기초8 (0) | 2024.08.10 |