메모리 동적 할당

메모리 정/동적 할당의 개념과 동적할당함수의 기능과 사용법, 메모리 관리 함수.

  • 학습 목표
    1. 메모리 정적할당과 동적할당의 이해.
    2. 메모리 동적할당함수를 이용하여 프로그램 작성.
    3. 메모리 관리함수의 기능 이해.

주요 용어

  • 데이터 영역 : 전역변수와 static 변수가 저장되는 기억공간 영역.
  • 힙(heap) 영역 : 프로그래머의 필요에 의해 할당과 소멸이 이루어지는 기억공간 영역.
  • 스택(stack) 영역 : 지역변수와 매개변수가 저장되는 기억공간 영역.
  • 메모리 정적할당 : 프로그램이 작성되는 단계에서 기억공간의 크기가 결정되는 기억공간 확보방법.
  • 메모리 동적할당 : 프로그램이 실행되는 중에 입력되는 자료에 따라 기억공간의 크기를 결정할 수 있는 기억공간 확보방법.

메모리 동적할당의 개념

C 언어에서의 기억공간?

  • 프로그램의 실행을 위해 기억공간 필요.
  • 기억공간은 운영체제에서 할당.
  • 할당되는 기억공간의 영역.
    • 데이터 영역.
      • 전역변수와 static 변수가 저장되는 영역.
      • 프로그램이 시작하면서 할당되고, 종료하면 소멸하게 됨.
    • 힙 영역
      • 개발자의 필요에 의해 할당/소멸이 이루어지는 영역.
      • 프로그램이 실행되는 중에 크기가 변함.
      • 메모리 동적 할당에 사용되는 영역.
    • 스택 영역
      • 지역변수와 매개변수가 저장되는 영역.
      • 함수 호출이 완료되면 사라짐.

기억공간의 확보

메모리 정적 할당(Static Allocation)

  • 기억공간의 데이터 영역스택 영역 이용.
  • 프로그램을 작성하는 단계에서 필요한 기억공간의 크기를 결정.

메모리 동적 할장(Dynamic Allocation)

  • 기억공간의 힙 영역 이용.
  • 프로그램 실행 중에 입력되는 자료에 맞게끔 기억공간의 크기를 결정.

메모리 정적할당

  • 변수 선언이나 배열 선언과 같이 프로그램을 작성하는 단계에서 필요한 기억 공간의 크기를 결정.
    • 변수 선언과 같이 할당 시켜줘야 할 기억공간의 한계 크기를 명확히 알고 있을 경우 사용.
    • 메모리 정적 할당은 프로그램이 시작될 때 미리 기억공간의 크기를 고정하여 할당.

메모리 정적할당의 예

  #include <stdio.h>

  void test1(int);
  void test2(int);

  //  전역변수로 선언되어 데이터 영역에 할당.
  //  프로그램 종료될 때까지 존재.
  int a = 100;

  void main()
  {
    //  지역변수로 스택 영역에 할당.
    //  main()가 종료될 때까지 존재.
    int b = a;

    test1(b);
    test2(b);
  }

  //  test1이 호출되면,
  //  매개변수 c와 지역변수 d는 스택영역에 할당.
  //  test1이 종료되면 c, d는 지워짐.
  void test1(int c)
  {
    int d;
    d = c + 10;
    printf("%d\n", d);
  }

  //  test2가 호출되면,
  //  매개변수 e와 지역변수 f는 스택영역에 할당.
  //  종료되면 d, f는 지워짐.
  void test2(int e)
  {
    int f;
    f = e + 20;
    printf("%d\n", f);
  }

  //  main()가 종료되면 스택영역에 할당된 b도 제거.
  //  프로그램이 끝아면 a도 제거.

메모리 정적할당의 장/단점

  • 장점
    • 프로그램에서 사용하게 될 변수의 기억 공간의 크기를 명확히 알고 있다면,
      메모리 정정할당은 쉽게 기억 공간을 사용할 수 있고, 에러의 발생 확률을 줄일 수 있음.
  • 단점
    • 사용하게 될 기억 공간의 크기를 정확히 알지 못하거나, 사용되는 자료의 크기가 각각 차이가 심하다면,
      기억공간의 낭비를 가져오게 됨.

메모리 정적할당의 문제점

  #include <stdio.h>

  void main()
  {
    int size;
    //  배열의 크기를 변수로 선언하면 에러 발생?
    char a[size];

    printf("입력할 문자열의 크기? : ");
    scanf("%d", &size);

    printf("주소 : ");
    scanf("%s", a);

    printf("입력된 주소 %s\n", a);
  }
  #include <stdio.h>
  #include <stdlib.h>

  void main()
  {
    int size;
    char *a;

    printf("입력할 문자열의 크기? : ");
    scanf("%d", &size);

    //  메모리 동적할당.
    a = (char *)malloc(sizeof(char) * size);
    printf("주소 : ");
    scanf("%s", a);

    printf("입력된 주소 %s\n", a);
    free(a);
  }

메모리 동적할당 함수

malloc(), calloc(), realloc(), free() 등

메모리 동적할당의 장/단점

  • 힙 영역을 이용하여 프로그램 실행 중에 입력되는 자료의 크기에 맞게 기억 공간 확보.
  • 많은 자료를 처리하는 배열의 크기를 실행 시간에 정의해야 하는 경우에 유용.
  • 프로그램 실행 시 기억 공간의 크기를 지정/재조정 가능.
  • 시간이 지체되는 단점.

메모리 동적할당 순서

  • 기억공간을 동적으로 할당 받을 변수를 포인터를 이용하여 선언.
  • malloc() 등을 이용하여 기억공간을 동적으로 할당 가능.
  • 기억공간의 사용이 끝나면 free()를 이용하여 기억공간 해제.

malloc()

  • 인자로 할당 받고자 하는 기억 공간의 크기를 byte 단위로 전달.
  • 힙 영역에 그 크기만큼 기억 공간을 할당, 할당한 기억공간의 첫 번째 주소 반환.
  • void*로 명시하여 어떤 형이든 형 변환 가능.
  • 초기화 안됨(기억공간의 초기화를 위해서는 memset() 사용).
  • 형식 : void * malloc(size_t, number_of_bytes);
  • 기능 : number_of_bytes에서 주어지는 크기만큼 기억 공간을 동적으로 할당.
  • 예 : void * malloc(sizeof(int));

free()

  • 메모리 해제 함수.
    • 힙 영역에 할당된 공간은 프로그램이 종료될 때까지 유지됨.
    • 할당된 기억 공간을 해제하지 않으면, 공간 부족 현상 발생.
    • 명시적인 반남.
  • 형식 : void free(void *p);
  • 기능 : 동적으로 할당된 기억 공간을 해제할 때 사용.

메모리 동적할당 예 - 1

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

  void main()
  {
    //  1) 동적할당을 위한 포인터변수 선언.
    int *a;
    //  2) 기억공간 할당,
    //    - (int *)는 왼쪽의 포인터변수 a와 자료형을 일치시키기 위한
    //      강제 형변환.
    a = (int *)malloc(sizeof(int));

    if (a == NULL)
    {
      puts("FAILLURE OF ALLOCATION!");
      exit(1);
    }

    *a = 20;
    printf("VARIABLE a : %d\n", *a);
    //  3) 할당받은 기억공간 해제.
    free(a);
  }

메모리 동적할당 예 - 2

  #include <stdio.h>
  #include <stdlib.h>
  #pragma warning(disable : 4996)

  void main()
  {
    //  입력받을 문자 수 저장 변수 선언.
    int size;
    // 1) 동적 할당된 기억공간을 연결할 포인터.
    char *str;

    printf("INSERT SIZE OF STRING : ");
    scanf("%d", &size);

    // 2) 입력 받을 문자 수(size + 1)에 맞게 동적 할당.
    str = (char *)malloc(size + 1);

    if (str == NULL)
    {
      puts("FAILLURE OF ALLOCATION!");
      exit(1);
    }

    printf("INSERT STRING : ");
    // 동적으로 할당된 기억공간에 문자열 저장.
    scanf("%s", str);

    printf("STRING THAT SAVED AT DYNAMIC MEMORY : \n%s\n", str);

    // 3) 할당받은 기억공간 해제.
    free(str);
  }

calloc()

  • malloc()와 동일하게 힙 영역에 기억공간 할당.
  • 사용하는 형태와 할당된 기억 공간을 0으로 초기화.
  • 형식 : void * calloc( int n, int size );
  • 기능 : 주어진 size의 크기를 갖는 기억 공간 n개를 할당.
  • 예 : void * calloc( n, sizeof(int));
  #include <stdio.h>
  #include <stdlib.h>

  void main()
  {
    int i;
    int *a;

    // int형 크기의 기억공간 5개 할당.
    a = (int *)calloc(5, sizeof(int));

    for (i = 0; i < 5; i++)
    {
      //  0으로 초기화 되어 있음.
      printf("%d\n", a[i]);
    }

    free(a);
  }

realloc()

  • 이미 할당 받은 기억 공간의 크기를 변경해야 할 필요가 있을 때 사용.
  • 형식 : void * realloc( void *p, int size);
  • 기능 : 포인터 p가 가리키고 있는 기억공간의 크기를 지정된 size의 크기로 변경.
  int *a;
  //  int형 크기의 5개 기억공간 할당.
  a = (int *)calloc(5, sizefo(int));
  ...
  //  int형 크기의 10개 기억공간 재할당.
  a = (int *) realloc(a, 10 * sizeof(int));

기억공간 관리함수

memcmp(), memcpy(), memset()

memcmp()

  • 기억 공간에 들어 있는 자료를 주어지는 크기만큼 비교하여, 같은지 여부를 알 수 있게 해주는 함수.
  • 형식 : int memcmp( void *s1, void *s2, size_t n);
  • 기능 : s1, s2가 가리키고 있는 기억 공간의 내용을 n byte 만큼 비교.
    • n바이트만큼 비교하여 s1 > s2 면 양수, s1 < s2 면 음수, s1 == s2 면 0 반환.
  #include <stdio.h>
  //  기억공간 관리함수를 위한 헤더파일.
  // #include <mem.h>
  #include <memory.h>

  void main() {
    char *s1 = "aaa";
    char *s2 = "bbb";

    int stat;

    stat = memcmp(s1, s2, 3);

    printf("%d\n", stat);
  }

memcpy()

  • 기억 공간의 자료를 다른 기억 공간 영역으로 복사하기 위한 함수.
  • 형식 : void * memcpy( void *dest, const void * src, size_t n);
  • 기능 : src에서 n byte만큼 dest에 복사.
  #include <stdio.h>
  #include <memory.h>
  #include <string.h>

  void main()
  {
    char src[] = "1234567890";
    char dest[] = "abcdefghijklmnopqrstuvwxyz";
    char *stat;

    printf("BEFORE MEMCPY() DEST : %s\n", dest);

    //  src의 첫 부분에서부터 문자열의 길이(strlen())만큼의 자료를 dest에 복사.
    stat = (char *)memcpy(dest, src, strlen(src));

    if (stat)
      printf("AFTER MEMCPY() DEST : %s\n", dest);
    else
      printf("FAILURE ON MEMCPY()!");
  }
  //  BEFORE MEMCPY() DEST : abcdefghijklmnopqrstuvwxyz
  //  AFTER MEMCPY() DEST : 1234567890klmnopqrstuvwxyz

memset()

  • 기억 공간의 자료를 지정한 문자로 채우는 함수.
  • 할당된 기억 공간의 초기화나 내용 삭제를 위해 주로 사용.
  • 형식 : void * memset( void *s, int c, size_t n );
  • 기능 : 포인터 s가 가리키는 곳을 c 값으로 n byte 만큼 채운다.
  #include <stdio.h>
  #include <memory.h>
  #include <string.h>

  void main()
  {
    char s[] = "1234567890";

    printf("BEFORE MEMSET() : %s\n", s);

    //  배열 s의 데이커를 *로 길이 만큼 채움.
    memset(s, '*', strlen(s));

    printf("AFTER MEMSET() : %s\n", s);
  }

728x90
반응형

'Language > C' 카테고리의 다른 글

[C] 파일 처리 함수 - 2  (0) 2021.01.02
[C] 파일 처리 함수 - 1  (0) 2021.01.01
[C] 구조체와 공용체 - 2  (0) 2021.01.01
[C] 구조체와 공용체 -1  (0) 2020.12.27
[C] 배열과 포인터 - 3  (0) 2020.12.27

함수와 기억 클래스 - 2

함수의 호출방법, 호출 함수와 피 호출함수사이의 자료전달방법, 변수의 유효범위아 기억클래스의 개념.

  • 학습 목표

    1. 값에 의한 자료 전달 방법과 참조에 의한 자료전달방법의 차이 이해.
    2. 지역 변수와 전역변수의 이해.
    3. 기억클래스의 의해.

주요 용어

  • 기억 클래스 : 변수를 기억공간의 특정 영역에 할당하는 방법.
  • 지역 변수 : 특정 범위 내에서만 사용되는 변수.
  • 전역 변수 : 프로그램 전체에 걸쳐 사용될 수 있는 변수.
  • 자동 변수 : 함수 실행 시 만들어 지고 실행이 끝나면 기억공간이 제거되는 유형.
  • 정적 변수 : 프로그램이 끝날 때까지 기억영역이 유지되는 유형.
  • 외부 변수 : 함수외부에서 선언되어 프로그램이 끝날 때가지 기억영역이 유지되는 유형.
  • 레지스터 변수 : CPU내의 레지스터에 자료를 저장하고자 할 때 사용되는 유형.

매개변수 사이의 자료 전달

Call By Value - 값에 의한 자료 전달

  • 기본적인 자료전달 방법.

  • 실 매개변수와 형식 매개변수 사이에 값의 전달.

  • 호출한 함수의 실행이 끝난 다음 전달받은 값을 되돌려 받지 못함.

  • 구조

    •   func1(10, 20); // 함수호출(실 매개변수)
        ...
        int func(int x, int y) // 함수 정의 (형식 매개변수)
  • EX

    •   #include <stdio.h>
      
        void swap(int x, int y);
      
        int main()
        {
          int a = 3, b = 5;
      
          printf(":: Before Call a = %d, b = %d\n", a, b); //  ::  a = 3, b = 5
      
          swap(a, b);
      
          printf(":: After Call a = %d, b = %d\n", a, b);  //  :: a = 3, b = 5
          return 0;
        }
      
        void swap(int x, int y)
        {
          x ^= y;
          y ^= x;
          x ^= y;
          printf(">> Inside swap x = %d, y = %d\n", x, y); //  >> x = 5, y = 3
        }
  • 값에 의한 자료 전달 과정


Call By Reference - 참조에 의한 자료 전달

  • 호출함수와 피 호출함수의 매개변수 값을 서로 교환할 수 있는 자료전달 방법.

  • 값을 전달하는 것이 아니라 실 매개 변수의 값이 들어있는 주소 값이 전달됨.

  • 구조

    •   func1(&a, &b); // 함수호출 (실 매개변수)
        ...
        int func1( int * x, int * y) // 함수 정의 (형식 매개변수)

& : 주소 연산자.

호출 하는 곳에서는 &를 붙여서 주소를 전달하는 것을 명시적으로 표현.

* : 내용 연산자. (*가 붙은 변수를 포인터 변수라고 한다.)

호출 당하는 곳에서는 *를 붙여서 주소가 가르티는 내용을 사용함을 명시적으로 표현한다.

  • EX

    • 참조에 의한 자료 전달방법은 피호출 함수 내에서 값의 변화가 일어나면 실 매개변수의 값도 변화됨.

    •   #include <stdio.h>
      
        void swap(int *x, int *y);
      
        int main()
        {
          int a = 3, b = 5;
      
          printf(":: Before Call a = %d, b = %d\n", a, b); //  :: a = 3, b = 5
      
          swap(&a, &b);
      
          printf(":: After Call a = %d, b = %d\n", a, b); //  :: a = 5, b = 3
          return 0;
        }
      
        void swap(int *x, int *y)
        {
          *x ^= *y;
          *y ^= *x;
          *x ^= *y;
          printf(">> 4. Inside swap x = %d, y = %d\n", *x, *y); //  >> a = 5, b = 3
        }
  • 참조에 의한 자료 전달 과정


기억 클래스 (Storage class)

  • 변수를 기억공간의 특정영역에 할당하는 방법.
  • 즉, 각 변수의 유효범위와 존속기간을 설정.
    • 변수의 사용 위치에 따라.
      • 지역 변수.
      • 전역 변수.
    • 변수의 존속기간에 따라
      • 자동 변수.
      • 정적 변수.
      • 외부 변수.
      • 레지스터 변수.

지역변수와 전역변수

지역 변수 (local variable)

  • 특정 범위 내에서만 통용되는 변수.

  • 선언된 블록이나 함수 내에서만 사용 가능.

  • 함수에서 사용되는 매개변수에도 해당.

  • 지역변수 사용 예

    •   #include <stdio.h>
      
        void func();
      
        int main()
        {
          //  main()) 내부에 선언되어 main()에서만 사용가능한 지역변수
          int i = 10;
      
          printf("\n main i : %d", i);
      
          func();
      
          printf("\n main i : %d\n", i);
      
          return 0;
        }
      
        void func()
        {
          //  main()의 i와는 별개인 지역변수.
          int i;
      
          i = 20;
          printf("\n func i : %d", i);
        }
    •   #include <stdio.h>
      
        int main()
        {
          // BLOCK A
          int x = 2, y = 4;
      
          printf("A >> x : %d, y : %d\n", x, y);
      
          {
            //  BLOCK B
            int x;
            x = 5;
            y++;
            printf("B >> x : %d, y :%d\n", x, y);
          }
          printf("A >> x : %d, y :%d\n", x, y);
        }

전역 변수 (global variable)

  • 함수 밖이나 외부 파일에서 선언되어 프로그램 전체에 걸쳐 사용될 수 있는 변수
  int x, y; //  변수 x, y는 **프로그램 전체에서 사용가능한 전역변수**(함수 외부에서 선언됨)

  long func1 (int m, int n) {
    x++, y--;
    m++, n--;
    //  변수 m, n은 func1 내에서만 사용 가능한 **지역변수**.
  }

  void func2 () {
    int m, n;
    //  func2()의 **지역변수**.
  }

  void main(){
    int m, n;
    // main()에서만 사용 가능한 **지역변수**.
    long x;

    x = func1( 10, 20 );
    func2()
  }
  • EX

    •   #include <stdio.h>
      
        void func();
        int x;
        //  **전역변수**, 가급적 프로그램의 선두에 위치하는 것이 좋음.
        //  전역변수는 초기화 안 하면 **0으로 자동 초기화**.
      
        int main()
        {
      
          printf(" 1 ) x : %d\n", x);
      
          func();
      
          printf(" 2 ) x : %d\n", x);
      
          return 0;
        }
      
        void func()
        {
          //  전역 변수이기 때문에 어디서나 사용 가능.
          x++;
        }

전역 변수와 지역변수의 비교

  • 동일 범위 내에서는 지역변수가 우선.
  • 전역변수의 선언은 프로그램 선두에 위치.
  • 가급적 지역변수를 사용하는 것이 효율적.
    • 함수의 독립성 향상.
    • 디버깅 효율 향상.
    • 기억공간 절약.

변수의 기억 클래스 종류

  • 변수의 초기화, 존속기간, 유효범위에 따라 구별.
    • 자동 (auto)
    • 정적 (static)
    • 외부 (extern)
    • 레지스터 (register)

기억 클래스를 이용한 변수 선언

  • 형식 : 기억클래스 자료형 변수명;
  • 기능 :
    • 기존의 변수 선언문에 기억클래스만 기입.
    • 선언된 변수에 저장도니 자료는 해당 기억영역에 놓이게 됨.
  • 사용 예
    • auto int a;
    • static int b;
    • extern int c;
    • register int c;

자동변수

  • 함수 실행시 만들어지고, 실행이 끝나면 기억공간이 제거됨.
  • 예약어 auto를 사용 (생략 가능)
  • 통용 범위는 변수가 선언된 블록이나 함수 내로 한정.
  • 지역변수에 해당.
  • 초기화 필요.
  #include <stdio.h>

  int main()
  {
    int i = 1;
    auto int j = 2;
    { //  BLOCK A
      int i = 3;
      { //  BLOCK B
        int i = 4;

        printf("i in BLOCK B : %d\n", i);
        printf("j in BLOCK B : %d\n", j);
      }
      printf("i in BLOCK A : %d\n", i);
    }
    printf("i in BLOCK MAIN() : %d\n", i);

    return 0;
  }

정적변수

  • 기억 영역이 프로그램 끝날 때까지 유지.
  • 예약어 static 사용.
  • 전역 변수에 해당.
  • 변수의 값은 프로그램 실행 중 계속 유지.
  • 초기화가 없으면 0 으로 초기화 됨.
  #include <stdio.h>

  int main()
  {
    //  자동 변수 선언
    int a = 10;
    //  정적 변수 선언
    static int b = 20;

    {
      int a = 5;
      printf("a : %d b : %d\n", a, b);
      // a는 자동 변수이므로 블럭 안에서만 효력이 발생함.
    }
    printf("a : %d b : %d\n", a, b);

    return 0;
  }
  #include <stdio.h>

  void test();

  int main()
  {
    int i = 0;

    while (i < 3)
    {
      test();
      i++;
    }

    return 0;
  }

  void test()
  {
    auto int a = 0;
    static int s = 0;

    printf("auto : %d, statc : %d\n", a, s);

    ++a, ++s;
  }
  /**
   * auto : 0, statc : 0
   * auto : 0, statc : 1
   * auto : 0, statc : 2
   * 자동변수는 호출 될 때 마다 값이 초기화 되지만,
   * 함수 내에서 사용된
   * **정적 변수는 함수를 빠져 나가더라고 그 값을 유지**
   * (한번만 초기화 됨)
   */

외부 변수

  • 함수의 외부에서 선언.
  • extern 예약어 사용.
  • 전역 변수에 해당.
  • 초기화가 없으면 0으로 초기화 됨.
  • 다른 파일에서 외부변수로 선언된 변수의 값을 참조할 수 있음.
  #include <stdio.h>

  int i = 10, j = 20;

  void main()
  {
    //  외부 변수 선언
    //  (**생략 가능**, 변수 i가 선언되면, 선언된 위치 이하부터 그 갑ㅆ이 유효하기 때문.)
    extern int i;
    //  외부 변수 선언
    //  (**생략 불가**, 변수 k가 범위 바깥에 있으므로 )
    extern int k;

    //  변수 j = 20, j = 100이 동시 선언시, **지역변수(자동변수)가 우선**
    int j = 100;

    printf("i : %d, j : %d, k : %d\n", i, j, k);
    //  i : 10, j : 100, k : 50
  }

  int k = 50;
  #include <stdio.h>
  #pragma warning(disable : 4996)

  //  extern_ex() 함수 원형 선언.
  void extern_ex();
  //  전역변수 s 선언.
  char s[100];

  int main()
  {
    printf("INSERT STRINGS : ");
    scanf("%s", s);

    printf("String (%s) WILL STORE GLOBAL VARIABLE S.\n", s);
    extern_ex(); // extern_ex() 호출

    return 0;
  }
  #include <stdio.h>

  extern char s[]; // 변수 s를 외부 변수로 선언

  void extern_ex()
  {
    //  외부 변수 s 출력.
    printf("EXTERNAL VARIABLE S IS \n%s\n", s);
  }

레지스터 변수

  • CPU 내의 레지스터에 자료를 저장하고자 할 때.
  • 예약어 register를 사용.
  • 자동 변수와 동일한 속성.
  • 프로그램의 실행속도 증가를 목적으로 사용.
    (주로 반복문에서 카운터 변수로 사용)
728x90
반응형

'Language > C' 카테고리의 다른 글

[C] 배열과 포인터 - 2  (0) 2020.12.27
[C] 배열과 포인터 - 1  (0) 2020.12.25
[C] 함수와 기억 클래스 - 1  (0) 2020.12.20
[C] 선택 제어문과 반복 제어문  (0) 2020.12.20
[C] 입출력 함수와 연산자 - 2  (0) 2020.12.20

+ Recent posts