용어 정리

변수: 변하는 값을 저장하는 곳. 데이터를 저장하기 위해 메모리에서 공간을 할당 받음.

주소값: 변수에서 메모리에서 할당받은 공간의 시작 주소

포인터: 위의 주소값을 저장하는 변수(또는 상수)

상수 포인터: 다른 주소값을 가리키도록 바꿀 순 있으나, 그 주소에 있는 값을 바꿀 순 없음.

포인터 상수: 다른 주소값을 가리키도록 바꿀 수 없으나, 그 주소에 있는 값을 바꿀 순 있음.

 

포인터 선언

변수타입* 변수명;
변수타입 * 변수명;
변수타입 *변수명 = 주소값;

으로 선언할 수 있습니다. (*이 어디 붙어도 상관없습니다.)

int a = 10
int* b = &a;

연산자

& 연산자(주소 연산자)

&변수

이렇게 사용할 수 있다.

int a = 10;
print("%p", &a); // %p 는 주소값을 출력할 때 사용합니다.

이런 식으로 & 연산자를 사용하게 되면 변수 a의 주소값이 출력됩니다.

* 연산자(참조 또는 역참조 연산자)

* 연산자는 뒤에 오는 주소값에서 그 주소에 담겨있는 값에 접근하는 데 사용합니다.

*변수

예를 들어 "*&변수"를 하게 되면 변수의 주소값에서 그 주소에 담겨있는 값을 가져오게 되므로 변수의 값이 나오게 됩니다.

int a = 948;
int* b = &a;
printf("%p %d\n", &a, *&a);
printf("%p %d", b, *b);

결과는

(a의 주소값) 948

(a의 주소값) 948

이 나오게 됩니다.

포인터 연산

포인터의 덧셈과 뺄셈

포인터의 덧셈

int a = 50;
int *b = &a;

char c = 'c';
char* d = &c;

printf("%p\n", b); //0061FF14
printf("%p\n", d); //0061FF13

printf("%p\n", b + 1); //0061FF18
printf("%p", d + 1); //0061FF14

* 포인터 간의 덧셈은 안됩니다. (쓸 이유가 없음)

* 포인터에 x만큼 더하면 주소값은 (x * 자료형) 만큼 늘어납니다.

포인터의 뺄셈

int a[2] = {50, 100};

int *a1 = a;
int *a2 = a + 1;

printf("%d\n", *a);
printf("%d\n", *(a + 1));

printf("%p %d\n", a1, *a1);
printf("%p %d\n", a2, *a2);
printf("%d\n", a2 - a1);

* 배열 자체는 배열의 첫 번째 주소를 나타내므로 *a는 배열의 첫 번째 항목, *(a+1)은 배열의 두 번째 항목이 됩니다.

* a1은 배열의 첫 번째 항목의 주소, a2는 배열의 두 번째 항목의 주소이므로 a2-a1은 배열의 두 번째 항목과 첫번째 항목의 주소의 차가 됩니다.

* a2 - a도 똑같이 첫번째 항목과 두번째 항목의 주소의 차가 되고 이걸 이용하면 a2가 배열에서 위치한 위치를 알 수 있습니다.

* lowerbound나 upperbound를 사용할 때 반환값에서 배열을 빼면 검색한 항목의 위치(index)를 알 수 있습니다.

포인터의 곱셈과 나눗셈

불가능합니다.

Call By Value

#include <stdio.h>

void test(int a){
    a = 10;
    printf("in test function, variable a value: %d\n", a); //10
}

int main()
{
    int a = 90;
    printf("before invoke test function, variable a value : %d\n", a); //90
    test(a);
    printf("in main function, variable a value: %d", a); //90
    return 0;
}

함수의 인자에 변수의 값을 복사해서 전달해준다.

함수 내에서 값을 바꿔도 원래 변수의 값이 변하지는 않는다. 

Call By Reference

#include <stdio.h>

void test(int *a)
{
    *a = 10;
    printf("in test function, variable a value: %d\n", *a); //10
}

int main()
{
    int a = 90;
    printf("before invoke test function, variable a value : %d\n", a); //90
    test(&a);
    printf("in main function, variable a value: %d", a); //10
    return 0;
}

함수의 인자에 변수의 주소값을 전달해준다.

함수에서 참조 연산자를 이용해서 값을 바꾸면 원래 변수의 값도 변한다.

다중 포인터

포인터도 변수이므로 포인터에 대한 주소값을 저장하는 포인터를 만들 수 있습니다.

이것을 다중 포인터라 부르게 됩니다. 아래 예시를 보면

int a = 10;
int *b = &a;
int **c = &b;
printf("%p %p %p\n", &a, b, *c);
printf("%d %d %d", a, *b, **c);

포인터 c는 포인터 b의 주소값을 저장하는 2중 포인터인 것을 알 수 있습니다.

포인터를 선언할 때 변수타입* 변수명; 으로 한다고 했었는데 int **c = &b;에서 (int*)가 자료형이 됩니다.

자료형*변수명 = 주소값;

int *   *c        = &b;

 

3중 포인터 예시를 보면

int a = 10;
int *b = &a;
int **c = &b;
int ***d = &c;
printf("%p %p %p %p\n", &a, b, *c, **d);
printf("%d %d %d %d", a, *b, **c, ***d);

이런 식으로 한 겹이 추가될 때마다 자료형에 *이 하나씩 늘어나게 됩니다.

728x90

+ Recent posts