2015년 8월 25일 화요일

C 언어 - 포인터 (메모리 관리)

메모리

이미테이션 게임이라는 영화를 보니 튜링이라는 사람이 컴퓨터 모델을 처음으로 구현한 사람으로 소개되었다.

영화에는 소개되지 않았지만 따로 위키를 검색해 보니 튜링 기계의 무한히 긴 띠가 컴퓨터 메모리에 비유된다고 설명한다.

긴 띠의 종이 폭 만큼의 데이터 조각 하나 당 크기를 갖을 수 있다.
데이터 조각이 쓰인 위치를 주소라고 부른다.

주소  데이터
0x00 00000000
0x01 00000000
0x02 00000000
0x03 00000000
0x04 00000000
...


  • 데이터 조각 하나를 바이트라고 생각하자.
  • 데이터 조각 여러개를 모아 하나의 의미를 이룰 때 short, int, float, double 또는 struct 를 사용한다.
  • 여러벌의 데이터 조각은 아래와 같이 데이터가 순차적으로 밀집되어 있다.
int a => 0x7ffc9a38dca4 0x7ffc9a38dca5 0x7ffc9a38dca6 0x7ffc9a38dca7

    • int 는 4 바이트 값으로써 시작 주소값은 운영체제가 정하지만 시작 주소값을 기준으로 순차적으로 4 개의 주소 값을 갖는다.


선언된 변수의 주소 값은 영역의 끝 또는 프로그램 종료 시 소유권을 포기한다.

정리


  1. 어느 위치에 메모리가 선언될지는 운영체제가 정한다.
  2. 우리는 메모리가 밀집되어 선언된다는 특성을 기억할 필요가 있다.
  3. 그리고 C 언어로 주소값을 이용해 성능 좋은 프로그램을 만들 수 있다.

포인터


 주소값 확인


int value = 10;
printf("%p\n", &value);

실행:
0x7ffc8461ced4


  • %p : 포인터 값 출력
  • & : 변수 앞에 입력하면 주소값 반환


포인터 변수 선언 및 값 참조


int * ptr_value = NULL;
int value = 10;

ptr_value = &value;

printf("%p\n", ptr_value);
printf("%d\n", *ptr_value);

실행:
0x7fff52d16d94
10


  • int * : 변수 타입에 * 를 붙이면 포인터 변수 선언
  • NULL : 없음을 의미하며 실제 값은 0
  • * : 포인터 변수 앞에 사용하면 값을 참조


포인터 변수는 값을 저장하지 않고 주소 값만 저장한다.

주소값으로 작업하기

 

함수로 보내기


#include <stdio.h>

static void s_set_value(int value) {
    value = 10;
}

static void s_set_value_pointer(int * value) {
    *value = 10;
}


int main() {
    int val = 0;
    s_set_value(val);
    printf("
s_set_value() : %d\n", val);

    s_set_value_pointer(&val);
    printf("
s_set_value_pointer() : %d\n", val);

    return 0;
}

실행:
s_set_value() : 0s_set_value_pointer() : 10


함수 파라메터로 넘어간 값은 메모리 복사된다.

s_set_value():
  1. s_set_value() 함수로 입력한 val 파라메터는 s_set_value() 함수의 int value 로 복사된다.
  2. s_set_value() 함수를 빠져나오면서 int value 는 제거된다.
s_set_value_pointer():
  1. s_set_value_pointer() 함수로 입력한 val 의 주소값은 s_set_value_pointer() 함수의 int * value 로 복사된다.
  2. s_set_value_pointer() 함수 안에서 *value = 10 을 통해 해당 주소에 위치한 데이터 공간에 10을 대입 시킨다.
  3. s_set_value_pointer() 함수를 빠져나오면 int * value 는 제거된다.
  4. val 값은 10으로 변경되어 있다.


배열을 포인터로 탐색하기


#include <stdio.h>
static void s_traverse_array_with_pointer(char * array) {

    char * p = array;

    while (*p) {
        printf("%c\n", *p);
        p++;
    }
}

int main() {
    s_traverse_array_with_pointer("hello");
    return 0;
}

실행:
h
e
l
l
o

  • while (*p) ; p 가 가리키는 값이 0 이 아니면 while 문을 수행한다.
  • p++ : 주소값에 1 증가 시킨다. (데이터가 순차적으로 밀집된 점을 이용)
  • "hello" : 문자열은 char 배열 형태이다.
hello 는 5글자로 이루어져 있지만 문자열의 끝을 표시하기 위해 6 바이트의 char 배열로 선언된다.

h e l l o \0
0 1 2 3 4 5

\0 : 문자 표기시 0은 \0 로 표기한다.


오래된 예제 : swap


#include <stdio.h>

static void s_swap(int * a, int * b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}
int main() {
    int a = 10;
    int b = 20;
    s_swap(&a, &b);
    printf("a: %d, b: %d\n", a, b);

    return 0;
}

실행:
a: 20, b: 10

  • temp: *a = *b 를 통해 a 값이 변경되기 때문에 기존의 a 값을 저장한 temp 변수가 필요하다.

프로그램 작성 시 임시 변수를 활용해 문제 해결할 수 있는 경우가 많으니 참고하자.

댓글 없음:

댓글 쓰기