본문 바로가기

포인터와 const (Pointer and Const) 본문

💘 C++/행렬, 문자열, 포인터, 참조

포인터와 const (Pointer and Const)

Hyonii 2022. 7. 16. 13:59

Pointing to const variables

일반 변수에 const를 사용해서 상수로 만들 수 있듯이 포인터에도 const를 사용할 수 있다.

그런데 일반변수와는 조금 다르다.

포인터와 const가 어떻게 사용되는지 설명드리겠다.

 

지금까지 보았던 모든 포인터는 상수(const)가 아닌 값을 가리키는 비-상수(non-const) 포인터다.

 

하지만 값이 상수인 경우에는 어떻게 될까?

value에 const를 걸어보자.

 

위 코드는 컴파일되지 않는다.

value가 const니까 포인터로 못 만드는 건가? 생각할 수 있지만 그렇지 않다.

pointer도 const가 앞에 붙으면 할 수 있다.

 

상수 변수는 값을 변경할 수 없다.

만약 상수가 아닌 포인터가 상수 변수를 가리킨 다음에 역참조하여 값을 바꿀 수 있다면, 

const의 의도를 위반하게 되므로 상수가 아닌 포인터는 상수 변수를 가리킬 수 없다.

 


상수를 가리키는 포인터 (Pointer to const value)

상수를 가리키는 포인터는 상수 변수의 주소를 가리키는 (non-const) 포인터다.

 

상수 변수에 대한 포인터를 선언하려면 자료형 앞에 const 키워드를 사용하면 된다.

 

위 예제의 ptr은 const int를 가리킨다.

 

대신 de-referencing을 못하게 되어있다.

 

그 의미는 value의 값을 못바꾼다는 것이다.

value의 값은 못 바꾸지만 그렇다고해서 value 변수의 주소를 못 바꿀 이유는 없다.

주소는 갖고 올 수 있고 ptr을 통해서 value를 읽을 수는 있다.

 

이렇게 de-referencing해서 출력하는 것은 문제없다.

 

value에 있는 것이 ptr을 de-referencing 통해서 5가 출력되는 게 확인되었다.

출력은 되지만 역참조해서 값을 assignment 하는 건 안된다.

 

value에다가 6을 넣는 것은 안된다.

*ptr = 6; 이 안되는 이유는 value가 6이 안되기 때문이고

value = 6; 이 안되는 이유는 앞에 const가 있어서 그렇다.

 


다른 예제로 넘어가서

value는 const가 아닌데 pointer는 const로 하면 문제 있을까?

 

이렇게는 쓸 수 있다.

상수 변수에 대한 포인터는 상수가 아닌 변수를 가리킬 수 있다.

상부 변수에 대한 포인터는 변수가 초기에 const로 정의되었는지에 관계없이 포인터를 통해 접근할 때 변수를 상수로 취급한다.

 

대신에

 

이렇게 해놓으면 value 자체는 const가 아니지만

ptr이 가리키고 있는 주소에 있는 값을 바꾸지는 않겠다는 의지를 보여주는 것이다.

 

대신 희한하게 이 경우에는

 

이건 가능하다

value가 5가 있고 5에 대한 포인터는 ptr이 갖고있는데

정작 ptr에서 역참조로 6으로 바꿀 수 없지만

그냥 원래 value에서는 6으로 바꿀 수 있다.

value를 출력하면 당연히 6이 나올 것이다.

이때 포인터를 출력하면 어떻게 나올까?

 

6이 나온다

ptr은 값은 못 바꾸지만 바꾼 곳은 가져올 수 있는 것이다.

바꾼 값은 가져올 수 있다는 의미다.

 


다음은 변수가 두 가지 있는 경우를 예로 들어보자

 

이런 상황에서 ptr에다가 value2의 주소를 넣는 것은 가능할까?

 

가능하다.

이것은

 

일반적인 변수 앞에 const를 붙일 때는 변수의 값을 바꾸지 않겠다는 의미이다.

 

포인터는 주소 값을 저장한다고 했다.

그런데 int *ptr 앞에 const를 붙이는 것은 내가 가리키고 있는 주소에 있는 값을 바꾸지 않겠다는 의미이지

이 ptr에 저장되는 주소 값을 바꾸지 않겠다는 의미는 아니다.

조금 헷갈리긴 하지만 이게 이해가 된다면 포인터는 거의 이해가 끝났다고 보면 된다.

 

아무튼 그래서 const를 붙였는데 ptr에 value1을 넣었다가 놀랍게도 ptr의 값을 바꿀 수는 없다.

예를 들어

 

이렇게 역참조를 했을 때에는 값을 바꿀 수 없다.

그런데

 

정작 포인터에다가 다른 변수를 넣는 것은 가능하다.

그리고 이 다른 변수를 다시 바꾸는 것은

 

불가능하다.

앞에 const가 붙어있기 때문이다.

 

이제까지 const value에 대한 포인터를 설명드렸다.

 


상수 포인터 Const pointer

포인터 자체를 상수로 만들 수 있다.

상수 포인터는 초기화 후에 가리키는 주소를 변경할 수 없는 포인터다.

 

상수 포인터를 선언하려면 자료형 뒤에 const 키워드를 사용하면 된다.

 

이번에는 int *const ptr = &value; 이런 경우에는 역참조가 될까?

 

역참조는 가능하다

그렇다면 역참조한 것에 어떤 값을 넣는 것은 가능할까?

 

이것도 가능하다

포인터가 상수일 뿐, 가리키는 변수는 상수가 아니므로 포인터를 역참조하여 값을 변경하는 것은 가능하다.

도대체 뭐가 안되는 걸까?

 

이번에는 위에서 설명드렸던 value2가 있는 경우 ptr에 &value2를 넣는 시도를 해보자.

 

이게 안되네;

이게 진짜 ptr에 있는 주소 값을 못 바꾸는 거다.

상수 포인터는 항상 같은 주소를 가리킨다.

 

이게 진짜 포인터를 상수로 만드는 const라고 생각한다.

int 앞에 const가 붙는 것은 pointing 하는 변수, 참조해주는 존재로서 const가 붙는 기능적인 문제이고

진짜 변수로서 포인터에 const가 붙는 것은 위 예제의 경우이다.

포인터 안에 어떤 메모리의 주소 값이 들어있을텐데 그 값은 못 바꾼다고 생각하면 된다.

 


상수를 가리키는 상수 포인터 (Const pointer to a const value)

상수를 가리키는 상수 포인터는 다른 주소를 가리키도록 수정할 수 없으며, 역참조를 통해 값을 수정할 수도 없다.

 

 int value = 5;가 있을 때

 

얘는 상수가 아닌 변수다.
이것은 충분히 된다.

이때는 포인터 자체가 상수라서 꼭 초기화를 해줘야 한다.

안 해주면 안 된다.

 

이렇게는 쓸 수가 없다.

뭔가 주소를 넣어주어야 한다는 의미다.

왜냐하면 이후에 바꿀 수 없기 때문이다.

 

그리고 앞에 또 const가 붙어있다.

그러니까 역참조해서 값을 바꿀 수 없다는 의미다.

 

이것도 안된다.

아무것도 안 되는 거다.

 


요약 (Summary)

정리를 해보자면

int value = 5;가 있을 때

 

한 변수(value)에 대해서 포인터가 여러 개 붙을 수 있다.

주소를 갖고 오는 것일 뿐이니까 전혀 상관없다.

 

이런 식으로 const가 붙는다는 것 알고 계시면 된다.

 

  • 비-상수 포인터는 다른 주소를 가리키도록 수정할 수 있다.
  • 상수 포인터는 항상 같은 주소를 가리키며, 가리키는 주소를 수정할 수 없다.
  • 상수를 가리키지 않는 포인터는 역참조를 통해 값을 변경할 수 있고, 상수값을 가리킬 수 없다.
  • 상수를 가리키는 포인터는 역참조를 통해 값을 변경할 수 없다.

이걸 어디서 쓰나 생각하실 수 있다.

상수를 가리키는 포인터는 주로 함수가 전달된 인수를 실수로 변경하지 않기 위해 매개변수(ex. 배열을 함수에 전달)에서 사용된다.

 

함수 파라미터로 들어갈 때 

특히 int *const ptr2 = &value; 이 들어가고 

const int *const ptr3 = &value; 도 간혹 들어간다.

함수 파라미터로 array를 집어넣을 때 결국은 포인터로 들어간다.

이때 "값을 바꾸지 마세요, 값도 바꾸지 말고 주소도 바꾸지 마세요"라고 아주 안전하게 코딩하고자 할 때 const를 넣는다.

 

const int *const 이렇게 쓰는 경우도 꽤 있다.

그리고 array 말고 다른 경우에도 const 포인터가 필요한 경우가 있어서 종종 보게 된다.

그럴 때 당황하지 않게 지금 잘 알아두자.

그리고 좀 더 실용적으로는 후에 참조를 배울 것이다.

포인터 참조를 배우게 되는데, 참조를 쓰게 되면 조금 더 타이핑하는 게 편해지고 깔끔해진다.

 

지금까지 포인터와 const를 사용할 때 어떠한 조합으로 사용되는지 경우에 따라 설명드렸다.

헷갈릴 수 있으니 한번 더 꼼꼼히 따져보자.

Comments