전공/운영체제

14. Memory API

nongdamgom 2024. 1. 4. 21:35

 

Memory API : malloc()

#include <stdlib.h>

void *malloc(size_t size)
  • size_t size => memory block sizke (byte)
  • size => unsigned integer type
  • void type pointer로 시작 주소가 return 됨 
  • 할당 실패 시 null pointer 반환

 

 

sizeof()

int *x = malloc(10 * sizeof(int));
printf("%d\n", sizeof(x));

/* malloc에 할당 되는 byte는 runtime에 잡히지만
sizeof() 자체는 macro라서 compile time에 수행 됨(포인터가 가리키는 곳 크기를 모름)
즉, 그냥 포인터 변수의 크기인 4가 출력된다. */


int x[10];
printf("%d\n", sizeof(x));
/* 정적 배열은 compile time에 크기가 할당되므로, 
sizeof가 compile time에 크기를 알 수 있음, 즉 int 배열 크기인 40이 출력 됨 */

 

 

 

Memory API : free()

#include<stdlib.h>

void free(void* ptr) // 할당한 공간의 시작 주소를 넘긴다
  • 시작 주소만 알고 전체 공간을 할당 해제 할 수 있는 이유?
  • => 할당 할 때부터, 추가 8~16byte 정도 더 할당해서, 여기에 크기 정보를 기록해 놓음 

 

 

Memory Allocating & freeing

int *pi;
pi = (int *)malloc(4 * sizeof(int));
free(pi);
  • free 한다고 해서, pi 변수 내의 저장된 값(free한 공간의 시작 주소값)이 지워지는건 아님.
  • 나중에 이 변수로 뭔가를 하려고 하면 에러 날 수 있다.

 

 

Forgetting To Allocate Memory

char *src = "hello";
char *dst;
strcpy(dst, src);
  • strcpy를 하면 scr에 있는 내용을 dst에 카피 하게 됨.
  • 정적으로 char*를 바로 잡아서 hello는 hello\0로 code section에 들어가게 됨.
  • dst는 초기화를 해주지 않아서, 쓰레기 값이 들어가게 되는데, 그러면 카피를 할 때, OS에게 허가 받지 않은 공간에 hello를 할당 해 버릴 수가 있음 => segmentation fault

 

Solution

char *src = "hello";
char *dst = (char *)malloc(strlen(src) + 1);
strcpy(dst, src);
  • dst를 미리 malloc 으로 초기화 해주면, 공간 허가를 받은 곳에 strcpy를 진행하므로 에러가 없다.
  • strlen은 null을 제외하고 복사하므로, malloc시 +1을 해줘야 nullptr 공간을 확보할 수 있다.

 

Not Allocating Enough Memory

char *src = "hello";
char *dst = (char *) malloc(strlen(src));
strcpy(dst, src);
  • 만약 nullptr 공간을 확보하지 않은채 strcpy를 하면 어떻게 될까?
  • Incorrect code 이지만, 돌아가는데는 문제가 없다.
  • strcpy는 무조건 null이 나올 때까지 copy를 한다.
  • 이 때, 위에서 말 했듯이, malloc이 넉넉하게 공간을 할당하기 때문에 null 공간을 확보하지 않아도 잘 돌아간다.

 

 

Forgetting to Initialize

int *x = (int *) malloc(sizeof(int));
printf("*x = %d\n", *x);
  • 첫 줄로 x에 공간의 크기는 할당하였지만, 정작 그 할당한 공간의 내용은 초기화 되지 못함
  • 공간 내의 값이 쓰레기 값으로 채워지게 됨 .
int *x = (int *) malloc(sizeof(int));
memset(x, 0, sizeof(int));
printf("*x = %d\n", *x);
  • memset을 이용하여 초기화를 해주면 해결이 가능하다.
  • memset(초기화 할 곳의 포인터, 초기화 할 값, 그 공간의 사이즈)
  • memset은 <string.h>에 있다.

 

 

Memory Leak

  • 할당 받은 공간을 사용하지 않는다면 free를 꼭 해줘야 memory leak을 막을 수 있다. 
  • free()하지 않는다면, memory 공간이 꽉 차서 run out of memory 에러가 날 수 있음.

 

 

Dangling pointer

  • 어떤 pointer가 이미 free 된 공간을 가리키고 있을 때 dangling pointer 라고 함. 
  • list에서 중간 node를 해제한다면, tmp node를 하나 만들어서 앞뒤로 연결을 해준 후 free 해줘야 한다. 

 

 

Other Memory APIs : calloc()

#include <stdio.h>

void *calloc(size_t num, size_t size)

/* 예시 */
malloc(10 * sizeonf(int))
calloc(10, sizeof(int))
// 둘은 같음
  • size_t num : number of blocks to allocate
  • size_t size : size of each block(in byte)
  • malloc과 다르게 초기화를 자동으로 0으로 해준다
  • => 자동 0 초기화 때문에 느림, mallock + memset이 성능이 더 좋다.

 

 

Double Free

int *x = (int *)malloc(sizeof(int));
free(x);
free(x); // undefined error

/* solution */
free(x);
x = NULL; // (void *) 0 를 가짐

/* 나중에 free 하고 싶을 때 */
if(x != NULL)
	free(x); // 한번 더 확인 할 수 있음

 

 

 

System Calls

#include <unistd.h>

int brk(void *addr); 
void *sbrk(intptr_t increment);
  • malloc 이 heap 공간을 더 할당 받고 싶을 때 brk 라는 system call을 사용하여 OS에 요청
  • heap의 끝부분을 break라고 부름
  • brk : 새로운 break의 절대 주소를 인자로 받음
  • sbrk : 새로운 break와 이전의 차이 값을 인자로 받음

=> 프로그램은 brk나 sbrk를 절대로 directly call 할 수 없다. (malloc에 의해 자동 호출)