C-Project14

‍우건우·2024년 2월 6일

스택(stack): 정적할당. 함수 내부에 선언한 지역변수, 매개변수는 스택에 생성됨. 이 때 컴파일러가 알아서 메모리를 확보함.

ex) int arr[100];

힙(Heap):동적할당. 프로그램 실행 중에 할당이 가능한 영역. 아직 사용전인 데이터를 저장하기 위해서 메모리를 미리 확보하면 나중에는 사용가능한 메모리 공간이 부족해진다.따라서, 메모리가 필요할 때 할당 받고 사용 후에는 반환을 해줘야 함.

stdlib.h에 선언된 세 가지 메모리 할당 함수 중 하나를 호출해야 함.
ex)
malloc(memory allocation): 메모리 블록을 할당하지만 초기화는 하지 않습니다.(쓰레기 값으로 채움)
calloc(clear allocation): 메모리 블록을 할당하고 초기화까지 합니다.(데이터 타입의 기본값으로 채움)
realloc(re allocation): 이전에 할당된 메모리 블록의 크기를 조정합니다.
free: 사용이 끝난 힙 메모리를 시스템에 반환.(동적할당한 메모리를 해제하기 위해 사용)

NULL 포인터: 메모리 할당 함수가 호출되면 요청을 충족할 만큼 큰 메모리 블록을 찾지 못할 가능성이 있습니다. 이 경우 함수는 NULL 포인터를 반환합니다. NULL 포인터는 "없음 포인터"라는 특수한 포인터 입니다. 메모리 할당시 함수의 반환값을 테스트하고 NULL 포인터인지 확인하여 적절한 작업을 수행하는 것이 좋습니다.

스택공간에 동적할당된 메모리 주소를 가리킬 포인터를 생성하고 힙영역으로 주소를 지시하도록 함

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

int main(void) {

	int* ptr; //스택공간에 동적할당된 메모리 주소를 가라킬 포인터 생성
	//동적할당 후 포인터로 주소를 지시하도록 함
	ptr = (int*)malloc(sizeof(int)); //int 데이터 하나를 저장할 공간 할당
	if (ptr == NULL) { //힙에 할당할 공간이 없으면 할당이 실패하면서 NULL 포인터 반환
		printf("할당실패\n");
		return 0;
	}
	else {//할당에 성공한 경우, 할당받은 공간에 데이터를 저장
		*ptr
			= 10;
		printf("*ptr: %d\n", *ptr);
	
		free(ptr); //할당이 실패한 경우 free를 호출하게 되면 시스템이 죽음
		ptr = NULL; //가리키는 대상이 없어져 NULL을 가리키도록 조정
	}
	
	return 0;
}

배열 동적할당

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

int main(void) {

	int* ptr; //스택공간에 동적할당된 메모리 주소를 가라킬 포인터 생성
	//동적할당 후 포인터로 주소를 지시하도록 함
	ptr = (int*)malloc(sizeof(int)*5); 
	if (ptr == NULL) { //힙에 할당할 공간이 없으면 할당이 실패하면서 NULL 포인터 반환
		
		//if(!ptr) == if(ptr ==NULL)
		//if(ptr) == if(ptr != NULL)
		
		printf("할당실패\n");
		return 0;
	}
	else {//할당에 성공한 경우, 할당받은 공간에 데이터를 저장
		ptr[0] = 10;
		ptr[1] = 20;
		ptr[2] = 30;
		ptr[3] = 40;
		ptr[4] = 50;
		for(int i=0;i<5;++i)	
			printf("ptr[%d]: %d\n", i,*(ptr+i));
	
		free(ptr); //할당이 실패한 경우 free를 호출하게 되면 시스템이 죽음
		ptr = NULL; //가리키는 대상이 없어져 NULL을 가리키도록 조정
	}
	
	return 0;
}

memset함수를 이용한 메모리 초기화

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

int main(void) {

	int* iptr;
	iptr = (int*)malloc(sizeof(int));
	
	printf("%d\n", *iptr); 
	memset(iptr,0,sizeof(int));
	printf("%d", *iptr);
	
    free(iptr);
    iptr = NULL;
	return 0;
}

calloc, realloc함수 활용

#include<stdio.h>
#include<stdlib.h>
void print(int* arr, int n);

int main(void) {

	int* iptr = (int*)calloc(10, sizeof(int));
	
	if (iptr) {
		for (int i = 0; i < 10; ++i) {
			printf("iptr[%d]: ",i);
			scanf_s("%d", &iptr[i]);
		}

		iptr = (int*)realloc(iptr, sizeof(int) * 20);

		for (int j = 10; j < 20; ++j) {
			printf("iptr[%d]: ",j);
			scanf_s("%d", &iptr[j]);
		}
		print(iptr, 20);
		free(iptr);
		iptr = NULL;
	}
	else {
		printf("동적 할당 실패\n");
		return 0;
	}
	
	return 0;
}

void print(int* arr, int n) {
	int i;
	for (i = 0; i < n; ++i)
		printf("%d ", *(arr+i));
	printf("\n");
}

열거형(enum): 상수를 정의하는 한 가지 방법.

enum이라는 키워드로 시작하여 tag이름을 사용할 수 있습니다. tag 이름을 뒤따르는 중괄호 안에 상수로 사용될 이름들을 지정해 줍니다. 열거형은 값을 설정하지 않으면 맨 앞의 멤버부터 0,1,2,3순으로 자동으로 부여됨. 첫번째 멤버에 특정값을 지정하면 뒤따르는 멤버들은 1씩 증가해서 부여됨. 상수를 정의할 수 있으며 프로그램에서 가독성을 올려줌.
열거형을 이용한 ex

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

typedef enum weapon_type {
	WT_KATANA,
	WT_BROADSWORD,
	WT_LONGSWORD,
	WT_HALBERD
}weaponType;

void attackMonster(weaponType type, int* monsterHP, int* damage) {
	switch (type) {
	case WT_KATANA:
		*monsterHP -= damage[WT_KATANA];
		break;
	case WT_BROADSWORD:
		*monsterHP -= damage[WT_BROADSWORD];
		break;
	case WT_LONGSWORD:
		*monsterHP -= damage[WT_LONGSWORD];
		break;
	case WT_HALBERD:
		*monsterHP -= damage[WT_HALBERD];
		break;
	}
}

int main(void) {

	weaponType myWeapon;

	int monsterHP = 50;
	int damages[4] = { 5,10,20,30 };

	printf("롱소드를 구매하여 장착합니다.\n");
	myWeapon = WT_LONGSWORD;
	attackMonster(myWeapon, &monsterHP, damages);
	printf("공격 후 몬스터 체력: %d", monsterHP);
	return 0;
}

0개의 댓글