point 가리키다

포인터는 C계열 언어에 특화된 문법이다. 

 

포인터는 아래와 같이 선언한다. *연산자를 붙인다.

int *ptr;

포인터는 두가지 방식으로 표현된다.

*ptr // 포인터가 가리키는 저장소의 값
ptr // 포인터의 값 (메모리 주소)

*ptr은 포인터가 가리키는 메모리의 값이다.

 

ptr은 포인터의 값이다. 메모리 주소값이 들어있다. 32비트 앱에서는 4바이트, 64비트 앱에서는 8바이트이다.

 

다음의 예제는 포인터의 정보를 보여준다.

#include <stdio.h>

void main(){

    int number1 = 256;
    int *ptr = &number1;

    printf("Var value      : %d\n", number1);
    printf("Var size         : %d byte\n",sizeof(number1));
    printf("Pointer value : %p\n",ptr);
    printf("Pointer size   : %d byte\n",sizeof(ptr));
    printf("Pointer value size   : %d byte\n",sizeof(*ptr));
    printf("Pointed size   : %d\n",*ptr);

}

int형 변수를 선언하고 이 변수에 포인터를 할당했다. 주소 연산자 & 를 사용하여 변수의 주소를 읽어오고 포인터에 할당한다.

 

변수의 값과 포인터가 가리키는 값이 동일하게 256 이다. 이것은 포인터로 변수의 값을 조작할 수 있다는 의미다.

 

변수의 사이즈 (int 4바이트)와 포인터가 가리키는 값의 사이즈 (4바이트)는 같다.

 

Pointer Value는 포인터의 값이다. 메모리 주소 값으로 16진수로 표현한다. 사이즈가 32비트면 4기가 64비트면 16엑사바이트의 주소에 접근할 수 있다. (이론상이다. 테라 이상의 메모리 크기에 대하여는 아직 개념이 없는 것 같다)

 

 

* 포인터로 값 조작하기

#include <stdio.h>

void main(){
    
    int number1;
    int *ptr;
    ptr = &number1;
    *ptr = 7000;

    printf("Variable value : %d\n", number1);
    printf("Pointed value   : %d\n",*ptr);

}

포인터에 할당된 주소로 찾아가서 값을 조작할 수 있다. 간접적으로 찾아갈 수 있는 것이다. int number는 main 함수의 지역변수이고 스택에 할당되어 있다. 포인터는 스택이건 어디건 제한없이 접근이 가능하다. 단 시스템에 의하여 보호되고 있는 지역(커널,H/W 영역)에 애플리케이션이 접근하면 기본적으로 접근이 되지 않는다. 단 OS의 권한을 가져오면 시스템 영역을 조작할 수 있기 때문에 100% 보호되는지 알 수 없다. 때문에 포인터를 사용할 때는 충분한 주의를 기울여야 한다.

 

* 함수로 조작하기

#include <stdio.h>

void Scope(int *ptr){
    *ptr = 58;
}

void main(){

    int num1;
    int *ptr;
    ptr = &num1;
    *ptr = 42; 
    printf("before, value *ptr : %d\n", *ptr);
    Scope(ptr);
    printf("after, value *ptr : %d\n", *ptr);
    printf("after, value *num1 : %d\n", num1);
}

본래 함수를 호출해서 인수를 넘겨주면 함수는 자신의 지역변수로 복사한다. 복사한 값을 매개변수라고 하며 이들의 값을 조작해도 본래 호출한 곳의 인수으 값은 바뀌지 않는다. 이것이 지역변수의 의미이다. 인수를 복사하니까 용량은 좀 늘어나겠지만 서로의 값을 분리하고 보호한다는 취지가 있다.

 

그런데 인수로 주소값을 넘겨주면 이야기가 달라진다. 주소 연산자 &를 사용해서 64비트 주소값을 전달한다. 인수는 주소값이고 호출한 곳의 값은 변하지 않는다. 주소값은 매개변수에 복사된다. 이때 포인터 변수로 받으면 (int *ptr) 지역에 포인터 변수가 생성되면서 인수로 넘어온 변수의 값을 조작할 수 있게 된다. 따라서 전통적 사용방식인 return 과 무관하게 void 형의 함수로 자신을 호출한 지역의 변수 값을 바꿀 수 있게 된다.

 

직접 값을 전달하는 것이 아니라 값이 저장된 위치(메모리 주소)를 받아오기 때문에 복잡해 보인다. 한번 거쳐서 간다고 생각하면 쉽다.

주소를 넘겨주면 값이 조종 가능하다.
결과창

함수 호출 후에 확인해보니 main 함수의 변수값과 포인터도 변경되었다. 값을 조작하는데 매개변수가 불필요하다. 예제에서는 메인에서 포인터를 만들어 주소를 넘겼지만 아래 두 문장은 같은 의미이다. 변수와 연산자가 어떻게 사용되는지 유의해서 본다.

 

Scope(&num1);


Scope(ptr);

 

* 예제 : 두 변수의 값 바꾸기

 

고전적인 함수를 만들어 본다. 두 변수의 값을 바꾸는 코드는 아래와 같다.

#include <stdio.h>

void main(){

    int a = 3;
    int b = 2;
    int temp = 0;

    printf("a: %d b: %d temp: %d\n", a, b, temp);

    temp = a;
    a = b;
    b = temp;

    printf("a: %d b: %d temp: %d\n", a, b, temp);

}

두 변수의 값을 바꾸려면 임시 변수가 하나 필요하다. 임시 변수를 두고 하나씩 밀어내는 방식이다.

 

a를 temp에 밀어넣고 a는 자신은 버리고 b를 받는다. 다시 b는 자신을 버리고 temp를 받는다. temp에는 a가 들어있었으니 두개의 수는 교환되었다.

 

여러개의 수를 처리하려면 함수를 만드는게 유리하다. 코드도 깔끔해질 것이다. 그런데 두개의 수를 바꾸려면 두개의 값을 리턴 받아야 한다. C언어는 반환하는 변수를 1개로 제한한다. 그러나 포인터를 사용해서 여러 변수를 반환받을 수 있다.

 

다음 예제는 그 중의 하나이다.

#include <stdio.h>

void Swap(int *ptr1, int *ptr2){
    int temp = *ptr1;
    *ptr1 = *ptr2;
    *ptr2 = temp;
}

void main(){

    int a = 7;
    int b = 3;

    printf("a: %d b: %d \n", a, b);
    Swap(&a,&b);
    printf("a: %d b: %d \n", a, b);

}

swap

이는 하나의 방법에 불과하다. 2개의 값을 바꿀 수 있으면 2개가 아니라 몇십개 수천개라도 바꿀 수 있다. 포인터를 어떻게 사용하느냐는 프로그래머가 통제 가능한 범위에 달려있다.

 

 

*참고자료 배열의 구조 - 메모리의 저장방식을 알고 싶을 때

 

자료구조 1 배열의 구조

C의 대표적 자료 구조인 배열에 대하여 알아본다. 배열은 대부분의 언어에 기본으로 장착되어 있는 자료형태이다. 컴퓨터는 모든 데이터를 0과1의 이진 데이터의 형태로 저장한다. 이진법과 C의

digiconfactory.tistory.com

포인터 기초에서 심화까지 - 인도 IT 강의

(IT쪽에 있어 인도 강의는 매우 훌륭하다, 영어가 어눌한 것도 우리나라랑 약간 비슷한듯)

 

공유하기

facebook twitter kakaoTalk kakaostory naver band