금융 경제 보험 정보

C++ 포인터 기초개념 , 1) 포인터가 뭐지? 본문

카테고리 없음

C++ 포인터 기초개념 , 1) 포인터가 뭐지?

정보캣 2023. 11. 3. 09:09

 

C++ 를 공부하면서 개념적으로 어려운 부분이 포인터이다. 왜냐하면 입문 프로그래머는 기존에 컴퓨터 프로그래밍을 해본적이 없기 때문에, 코드를 짠다는 것에 대한 개념자체가 없는 것이다.

그래서 직접적으로 메모리에 저장 되어 있는 변수를 불러온다는 개념자체를 이해하기가 어려웠다. 개인적으로 정리해보면 포인터는 주소를 통해서 변수에 할당 된 값을 불러와서 무엇인가를 하는것이다.

1.메모리에 저장된 변수에 할당 된 값이란 무엇인가?
우리가 컴퓨터에 명령을 내릴 때 결국 가장 low레벨로 뜯어보면 'A를 B 해라' 가 된다. 컴퓨터를 요리 기계라고 생각하면 '마늘(A) 을 썰어라(B) '가 된다. 결국 A라는 데이터를 B로 실행하라는 의미가 되고 여기서 A는 데이터 = 변수에 할당 된 값이 되는 것이다.

2. 각 함수 블록에 들어 있는 지역변수는 함수 블록을 벗어나서 공통적으로 사용 될 수 없다
그런데 각 함수 속에 들어 있는 마늘들은 그 함수를 벗어나서는 사용 될 수 없다.
예를 들어서

void 마늘썰어 ()

{

}

int main ()

{ string 마늘 = 우리집마늘;

std::cout << 마늘 <<std::endl;

return 0;

}

이렇게 각각 두개의 함수를 만들었다. 그런데 여기서 메인 함수에 들어 있는 지역변수인 '마늘'은 마늘썰어 함수에서 사용할 수 가 없다. 왜냐하면 지역함수의 이름은 '마늘'은 그냥 이름표 같은 개념이지 실제 저장되어 있는 메모리의 위치를 가르키는 것이 아니기 때문이다. 즉 이름을 붙이기에 따라서 지역변수 이름 '마늘'은 다른 함수에서도 똑같이 붙일 수가 있다. 마치 나는 우리집 마늘을 사용하려 했는데 남의 집 마늘을 훔쳐와서..

3. 변수의 주소값 이란?

우리가 실제 현실에서도 계약서를 쓸 때, 이름을 쓰고 사는 곳의 주소를 함께 쓴다. 왜냐하면 똑같은 이름인 동명이인이 있을 수 있기 때문이다. 그런데 사는 곳의 주소까지 쓰게 되면 이런 중복을 벗어나 진짜 특정대상 하나만 가리킬 수 있다.

예를들어 마늘을 특정 할 때에도 서울시 강남구 타워팰리스 1동 1호 1번 냉장고 첫번째칸 이라고 쓰게 되면 마늘이 겹치지 않는다.

이것과 똑같이 포인터를 이용하면 각 변수의 주소를 표시할 수 있다. 이미 변수가 데이터로 저장이 될 때 메모리에 공간을 할당 받게 되고, 그 공간은 16진수의 주소로 표시된다. 그래서 모든 매개변수들은 메모리에서 고유한 공간과 고유한 주소를 가지고 있다(변수도 자가 내집이 있는데......)

그래서 실제 주소를 표현 할 때, &지역변수명 으로 코드를 짜면 실제 주소를 확인해 볼 수 있다

#include <iostream>

int main(){

    int number = 10;
    std::cout << &number << std::endl;

    return 0;
}

아주 간단하게 코드로 확인해보면 int 정수라는 데이터 타입을 가지고 있는 number 라는 지역변수에다가 10이라는 실제 변수 값을 할당해 두었다. 그리고 &number 를 출력하면
0x16fab2a78
이런 주소가 출력이 된다.

4. 포인터도 하나의 변수이다.
포인터도 변수라는 개념을 이해해야 한다. 포인터는 이 각 변수의 주소를 저장해둔 또 다른 공간인 것이다. 앞서 이야기했던 마늘이라는 이름의 공간에는 우리집 마늘이라는 실제 변수값이 저장되어 있다면 포인터에는 특정 변수의 주소를 저장하고 있는 것이다. 그러니까 다시 말하면 &number가 저장되어 있는 주소는 0x16fab2a78 이 되는데 이 주소를 저장하고 있는 공간이 또 포인터이다. 즉, 포인터가 가리키는 데이터의 메모리 주소를 포함한다. 그래서 이름이 포인터이다. 남의 데이트 주소를 가르킨다고..


'1) 넘버라는 이름의 공간은 10이라는 변수를 0x16fab2a78에 저장하고 있다
2) 포인터라는 이름의 공간은 0x16fab2a78 라는 변수의 주소를 본인의 주소에 저장하여 가리키고 있다.

5. 포인터를 사용하는 형태

포인터를 사용할 때에는 두가지의 형태를 알아야 한다.
1) 포인터의 선언 : (int *ptr) 이렇게 변수의 이름 앞에 *를 두어서 이 변수는 포인터라는 것을 선언 할 수 있다.

2) 메쏘드 내에서 포인터의 역참조 :
포인터를 메쏘드 내에서 쓰게 되면 포인터가 가리키는 주소의 변수 값을 사용한다는 의미이다.

즉 아래에서 정리해 보면

1) int number = 10; number 의 변수 값은 10
2) &number, 즉 10이 저장 되어있는 공간의 주소는 0x16fab2a78
3) int *ptr = &number; , 즉 0x16fab2a78 주소의 실제 저장 되어 있는 변수 값은 10 (*ptr) (역참조)

이 되는 것이다. 뭔가 계속 뱅글뱅글 도는 느낌적인 느낌 ㅋㅋ

6. 포인터를 사용하는 이유?
그럼 결국 포인터는 어떤 지역변수의 변수값을 다시 지칭하는 것인데 이걸 도대체 왜함? 뭐하려고 이렇게 꼬아서 다시 변수값을 지정하지? 그 이유는 앞서 말했던 2번의 '각 함수 블록에 들어 있는 지역변수는 함수 블록을 벗어나서 공통적으로 사용 될 수 없다' 라는 것 때문이다.

이 문제를 포인터를 사용하게 되면 해결 할 수 있다. !

코드 예를 들어보면

#include <iostream>

void AddOne(int* ptr)
{
    *ptr += 1;
}

int main(){

    int number = 10;
    AddOne(&number); 
    std::cout << number << std::endl;

    return 0;
}

AddOne 이라는 함수는 어떤 매개변수의 주소의 특정 변수값에다가 1을 더하라는 함수이다.

이렇게 number 라는 지역변수를 함수 블록너머 에서 사용할 수 있는 이유는 포인터를 통해서 메모리의 주소 값에 직접적으로 액세스 할 수 있었기 때문에 변수의 중복 사용이라는 가능성을 없앨 수 있기 때문이다.

그래서 위의 코드에서 보는 것처럼 AddOne 함수에서 메인 함수의 지역 매개 변수 였던 'number'를 적용 시킬 수 있다!

Comments