[C] 포인터

2 minute read

포인터는 말 그대로 가르키는 것, 가르키는 행위 객체 정도로 이해할 수 있다. 포인터의 역할과는 상관없이 그 또한 객체이기 때문에 변수로 정의되고 활용될 수 있다. Data type에 대한 post에서 소개한 정수/실수/문자 자료형처럼 가르키는 행동을하는 변수가 포인터 변수이며, 이때 가르키는 대상은 메모리 주소이다. 즉 누군가의 메모리 주소를 가르키는 것이 포인터 변수이다. 포인터 변수를 표현할 때에는 * 연산자를 사용하게 되며 이를 indirect 연산자라고 부른다. 선언 및 초기화 이후 활용 형태가 * 연산자 유무에 따라 다르므로 유의해야 한다. 아래 예시를 살펴보면, 포인터 변수 선언시에는 * 연산자를 사용했고, 주소값을 직접 입력받을 때에는 * 연산자를 사용하지 않지만 값을 출력할 때에는 * 연산자를 사용했음을 알 수 있다. 처음 포인터를 배울 때에는 두 용례가 헷갈리기 쉬우므로 선언 및 초기화 때와 이후 변수를 사용할 때를 따로 기억하는 것이 좋다.

    #include <stdio.h>

    int main(){
        int *p;
        int a=1;

        p= &a;
        printf("%d %d", p, *p);

        return 0;
    }

6422036
1

변수를 선언할 때 * 연산자를 사용하여 포인터 변수임을 명시했다. 포인터 변수가 주소를 입력받을 때에는 * 연산자를 사용하지 않았으며, 가르키는 주소에 접근하여 그 값을 확인할 때에는 * 연산자를 사용했다. 포인터 변수가 주소를 저장하고 이를 바탕으로 이동, 편집 등을 하기 위해서는 대상의 data type 필요하다. 예시에서는 정수 자료형에 대한 주소를 입력받기 위해 포인터 변수 또한 int로 선언되었다. 이 포인터 변수는 int 변수 단위인 4B 단위로 이동하게 되며, 만약 char에 대한 포인터 변수일 경우 1B 단위로 이동하게 된다.

굳이 왜 이렇게 포인터 변수를 사용하는지에 대해 이해하기 위해서는 Call by value / Call by address에 대해 알고 넘어갈 필요가 있다. 위 예제에서 a 에 값을 전해주는 것과 달리 포인터 변수는 주소 값을 받고, 그 주소값을 통해 값에 접근한다. 주소 값을 호출하는 것이 값을 호출하는 것 보다 유리한 경우에 포인터 변수를 사용하게 되는데, 그 중 하나가 함수에서의 변수 사용이다. 전역변수와 지역변수 개념에 대해 이해하고 있다면, 입력으로 받지 않은 값에 대해서는 전역변수로 선언되지 않았다면 참조할 수 없다는 걸 알고 있을 것이다. 또한 배열과 같이 많은 값을 한번에 함수로 전달할 수 없다는 걸 알고 있다. C 언어는 이런 부분에서 포인터 변수를 사용하여 변수, 배열의 주소만 전달하여 활용함으로써 연산 시간이나 방법에서의 효율을 높이고자 했다.

배열과 포인터 변수에 대해서는 주소 관점에서의 동치관계를 이해할 필요가 있다. 간단한 배열 선언문 int a[10] 에 대해 살펴보면 a 배열의 정수 자료형 요소 10 개이다. 이때 a 자체는 포인터 변수와 동일한 역할을 한다. 즉 주소를 전달하는 역할을 수행한다. a[4] 와 같이 이미 선언된 배열에 접근하는 것은 a의 5번째 요소에 접근하겠다는 의미이므로 a 자체가 묶음 변수의 기준 역할을 해준다고 이해할 수 있다. 실제로도 배열의 이름만 사용하는 경우 주소로 활용가능하고 그때의 의미는 배열의 첫 번째 요소 주소와 일치한다. 그래서 scanf와 같은 함수에서 입력 값에 주소를 전달할 때, &a[0]를 전달하기도 하지만, a 를 그대로 전달하기도 하는 것이다.

 a== &a[0];
 *a = a[0];
 *(a+1)=a[1];
 *(a+2)=a[2];

 *(a+i)=a[i]
#include <stdio.h>

    void func(int *x){
        *(x+4)= 30;
        
    }


    int main(void){
       int x[5]={2,4,6,8,10};

       func(x); 
       printf("%d", x[4]);

        return 0;
    }


30

func(int *x) 는 주소 값을 입력으로 받으라는 표식이자 포인터 변수를 정의하고 이 포인터 변수의 형태로 입력 주소를 받겠다는 뜻이다. 즉 입력으로 주소를 받고, 이 주소를 함수내에서 정의한 포인터 변수에 저장하여 함수내에서 활용하겠다는 것이다. 이렇게 하면 함수내에서 입력 주소값을 타고 밖으로 나가 그 주소에 적혀있는 데이터에 접근 및 수정이 가능하게 된다.

먼 훗날 pointer 개념과 관련된 그림들이 업데이트 될 예정임