본문 바로가기

Software

Secure Cordng C - 포인터의 개념과 이해 #3

1-9 동적 메모리 할당


1. 동적 메모리 할당

  • 동적 메모리 할당 또는 메모리 동적 할당은 프로그램이 실행 시간(runtime) 동안
    또는 실행 중에 사용할 메모리 공간을 할당하는 것을 의미
  • 동적 할당은 스택(stack)이 아닌 힙(heap) 영역을 사용



2. 동적 메모리 할당 함수
  • 사용자가 동적으로 메모리를 할당할 수 있도록 표준 라이브러리에서는 다음의 3가지 함수를 제공하며
    이 함수는 sodlib.h 파일에서 찾을 수 있음
    1. void *malloc(size_t size): 힙 영역에 메모리 할당
    2. void *realloc(void *ptr, size_t size): 기존의 할당된 메모리의 크기를 변경
    3. void *calloc(size_t elength, size_t esize): 힙 영역에 메모리 할당 후, 0으로 초기화

  • 힙에 생성된 메모리는 프로그램이 종료될 때까지 유지되므로
    사용이 끝난 동적 할당 메모리는 반드시 해제해야 하며
    동적 할당된 메모리를 해제하는 함수는 다음과 같다
    1. void free(void *p): 동적 할당된 메모리를 해제

  • 동적 할당은 항상 성공하는 것이 아니며, 동적 메모리 할당에 실패할 경우, 동적 메모리 할당 함수는 널(NULL)을 반환
  • 동적 할당 함수 호출 후의 반환 값을 조사한 다음 사용해야 함
#include <stdio.h>
#include <stdlib.h>

int main()
{
//static allocation(stack)
int age = 10;
printf("age = %d\n", age);

//dynamic allocation(heap)
int *pAge = malloc(sizeof(int));
if(pAge == NULL){
fprintf(stderr, "malloc error");
return;
}

*pAge = 10;
printf("*pAge = %d\n, *pAge");

free(pAge);
}


3. 동적 메모리 할당의 필요성

  • 프로그램을 구현하는 단계에서 필요한 메모리의 크기를 결졍하는 것을 정적 할당이라고하며
    변수나 배열의 선언이 이에 해당됨
#include <stdio.h>

void main()
{
char names[3][32];    // 정적 할당
int i;

for(i = 0; i < 3; i++){
printf("Input your name: ");
scans("%s", names[i]);
}

for(i = 0; i < 3; i++)
printf("%d. %s\n", i + 1, names[i]);
}


  • 구현 단계에서 입력된 데이터의 범위를 예측하는 것은 어렵기 때문에 프로그램이 실행중에
    입력된 데이터의 범위에 맞게 메모리를 확보활 필요가 있으며 이를 동적 할당이라고 함
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void main()
{
char *names[3];
int i;

for(i = 0; i < 3; i++){
char buf[32];
printf("Input your name: ");
scans("%s", buf);

names[i] = malloc(strlen(buf) + 1);
strcpy(names[i], buf);
}

for(i = 0; i < 3; i++)
printf("%d. %s\n", i + 1, names[i]);

for(i = 0; i < 3; i++)
free(names[i]);

return 0;
}


4. 배열의 동적 할당

  • 1차원 배열의 경우, 배열의 전체 크기를 계산하여 그 크기만큼 힙에 할당하고
    할당된 객체의 시작 주소를 원소의 타입에 대한 포인터 변수로 가리키면 됨
#include <stdio.h>
#include <stdlib.h>

void main()
{
// static allocation(in stack)
int arr[10];
int i;
for(i = 0; i < 10; i++){
arr[i] = i;
printf("arr[i] = %d\n", i, arr[i]);
}

// dynamic allocation(in heap)
int *pArr = malloc(sizeof(int) * 10);
for(i = 0; i < 10; i++){
pArr[i] = i;
printf("pArr[%d] = %d\n", i, pArr[i]);
}
free(pArr);
}


  • 2차원 배열을 동적으로 할당하려면 2차원 배열의 전체 크기를 계산하여 그 크기만큼 할당하고
    할당된 객체의 시작 주소를 배열 포인터에 저장하면 됨
#include <stdio.h>
#include <stdlib.h>

void main()
{
// static allocation(in stack)
int arr[2][3] = { 0, };
int i, j;
for(i = 0; i < 2; i++){
for(j = 0; j < 3; j++)
printf("%d ", arr[i][j]);
}
getchar();

// dynamic allocation(in heap)
int (*pArr)[3] = calloc(2 * 3, sizeof(int));
for(i = 0; i < 2; i++){
for(j = 0; j < 3; j++)
printf("%d ", arr[i][j])
printf("\n");
}

free(pArr);
}


1-10 이중 포인터


1. 이중 포인터(Double Pointer to)

  • 포인터 변수의 주소를 저장하는 포인터 변수를 의미
#include <stdio.h>

void main()
{
int age = 10;
int *pAge = &age;
int **ppAge = &pAge;

printf("**ppAge = %d\n", **ppAge);
}


2. 이중 포인터를 사용하는 이유

  • 이중 포인터를 사용하는 이유는 일반적으로 다른 지역에 선언된 일중 포인터 변수에 접근하기 위함
#include <stdio.h>

void init_ptr(int **pPtr)
{
*pPtr = 0;
}

void main()
{
int *ptr;
printf("ptr = %d\n", ptr);

init_ptr(&ptr);
printf("ptr = %d\n", ptr);
}


1-11 함수 포인터

함수의 시그니처란? 함수의 선언에서 함수명을 뺀 나머지를 의미한다.


1. 함수 포인터(Pointer of Function)

  • 함수를 가리키는 포인터를 의미
  • 함수 포인터의 선언 방법: 리턴타입(*변수명)([매개변수,...]);
  • 함수의 주소를 얻는 방법: 함수의 이름은 함수의 시작 주소로 해석됨
  • 함수 포인터를 사용한 함수 호출 방법: 함수 호출(0) 연산자 사용
#include <stdio.h>

void foo(int a) { printf("called foo(%d)\n", a); }

void main()
{
void(*fptr)(int);
fptr = foo;
fptr(10);
}


2. 함수 포인터의 다양한 사용

  • 함수 포인터는 다른 함수의 인자 또는 반환 값으로 사용 가능
#include <stdio.h>

void foo(int a) { printf("called foo(%d)\n", a); }

void goo(void(*fp)(int), int a)
{
fp(a);
}

void(*hoo())(int)
{
return foo;
}

void(*zoo(void(*fp)(int)))(int)
{
return fp;
}

void main()
{
foo(10);
goo(foo, 10);
hoo()(10);
zoo(foo)(10);
}


3. 복잡한 함수 포인터 선언 문제 해결

  • typedef 키워드를 사용하여 해결
#include <stdio.h>

void foo(int a) { printf("called foo(%d)\n", a); }
typedef void(*FP)(int)

void goo(FP fp, int a)    //void goo(void(*fp)(int), int a)
{
fp(a);
}

FP hoo()    //void(*hoo())(int)
{
return foo;
}

FP zoo(FP fp)    //void(*zoo(void(*fp)(int)))(int)
{
return fp;
}

void main()
{
foo(10);
goo(foo, 10);
hoo()(10);
zoo(foo)(10);
}


1-12 가변 배열


1. 가변 배열(Jagged Array)

  • 기존의 2차원 배열의 동적 할당의 단점은 열이 고정되어 있음
  • 포인터 배열과 동적 메모리 할당을 조합하면 행과 열이 고정되지 않은 가변 배열을 생성할 수 있다.
#include <stdio.h>
#include <stdlib.h>

void main()
{
int i, j;
int row = 2, col = 3;
int **arr = malloc(sizeof(int*) * row);

for(i = 0; i < row; i++)
arr[i] = calloc(col, sizeof(int));

for(i = 0; i < row; i++){
for(j = 0; j < col; j++)
printf("%d ", arr[i][j]);
printf("\n");
}

for(i = 0; i < row; i++)
free(arr[i]);
free(arr);
}


반응형

'Software' 카테고리의 다른 글

Secure Cordng C - Preprocessor  (0) 2018.07.23
Secure Cordng C - Coding Style  (0) 2018.07.20
Secure Cordng C - 포인터의 개념과 이해 #2  (0) 2018.07.18
Secure Cordng C - 포인터의 개념과 이해 #1  (0) 2018.07.09
DSP란 무엇인가?  (1) 2016.11.21