언리얼 - C++ 19 : const

김정환·2025년 3월 19일

Unreal C++

목록 보기
19/37

상수 const

값이 바뀌지 않는 데이터

  • 개념적으로는 변수와 반대지만,
    실상은 변수에 대해 값을 수정하지 못하도록 제한한 것
const int cInt = 100;

// 상수화
// r-value
// 10, 122 과 같은 수
// const 키워드로 선언된 변수
cInt; 
int ii = 10;
  • r value : 바꿀 수 없는 값을 의미.
  • l value : 바꿀 수 있는 값을 의미.

상수에 대한 착각

const int cInt = 100;
  • 이 상수가 변하지 않을거라고 생각함.
    • cInt = 500; 이것과 같은 행위는 문법적으로 방지
  • 근데, 포인터를 사용하면?
    주소에 접근해서 값을 바꾼다면?
  // 문제의 소지가 있지만 상황 예시를 위함.
  const int cInt = 100;
  int* pInt = (int*)&cInt;
  *pInt = 300;
  
  printf("cInt 출력 : %d", cInt); // 100 vs 300
  • 컴파일하면 값이 변하지 않음.
  • 이유 :
    강제로 포인터로 넣은 값으로 변경했음.
    하지만 상수이기 때문에 컴퓨터는 메모리가 아닌
    CPU의 레지스터에서 상수값(100)을 출력해준 것.
    • 컴퓨터 입장에서는 상수라고 명명했기 때문에 메모리에서 읽어오는 것보단
      더 빠른 레지스터에서 읽어와 쓴 것. (컴파일러가 최적화)
    • 절대 변할리 없다는 전제가 있었기 때문임 : 상수
    • 하지만 메모리 상에서는 300이 들어가 있음

  • 예외 volatile 키워드

    volatile const int cInt = 100;
    int* pInt = (int*)&cInt;
    *pInt = 300;
    
    printf("cInt 출력 : %d", cInt); // 300
    • volatile : 휘발성.
      • 이 키워드를 붙이면 변수를 확인할때, 레지스터를 거치지 않고 메모리를 반드시 확인하도록 함.
      • 값이 휘발성이므로 레지스터 값을 믿지 말고 메모리를 확실하게 체크하라고 명시한 것.

const가 붙어 상수처럼 쓸 순 있으나,
값을 바꾸지 못하는 것은 아님. (절대적이진 X)

상수와 포인터

const 포인터

  • 꽤 자주 같이 사용.
  • 이 구문이 의미하는 것을 이해해야함
const int i = 100;

int a = 0;
int* pInt = &a;
  • 포인터를 이용해서 바꿀 수 있는 데이터?
    • a 수정
      • *pInt = 1;
      • 가리키는 대상의 을 변경
    • 포인터 변수 자체 (주소)
      • pInt = nullptr;
      • 가리키지 않도록 변경

포인터 변수의 상수화

  • 포인터와 const 연계하면 2가지 경우가 생김
  1. 포인터 변수가 가리키고 있는 곳을 상수로 사용하는 것 (값)
  2. 포인터 변수 자체가 상수가 되어버리는 것 (주소)
    • 처음 한 대상을 가리키면 다른 대상을 가리킬 수 없어짐.
  • 이는 const가 붙는 위치에 따라 달라짐.

1. 원본의 상수화

const 포인터

  • 포인터가 가리키는 값을 상수화
int a = 0;

const int* pConstInt = &a;
*pConstInt = 100;	// 불가능. 문법 오류. lvalue(: 값이 바뀔 수 있는 변수)가 아니라 수정 불가

int b = 0;
pConstInt = &b;		// 가능
  • 접근까진 되는데 값을 바꿀 수 없음.
    • 원본(값)을 상수화

2. 포인터 자체의 상수화

포인터 const

int a = 0;
int* const pIntConst = &a;

*pIntConst = 100;	// 가능

int b = 0;
pIntConst = &b;		// 불가능
  • 포인터 변수 자체가 상수화되어 다른 주소를 받을 수 없음.
    • pIntConst는 a만을 가리키는 포인터가 됨.

조합에 따른 두 가지 동시 사용

const int* const pConstIntConst = nullptr;
  • 두 가지 모두를 상수화할 수 있음.
    • 주소를 바꿀수도 없고, 값을 바꿀수도 없음.

두 사용법이 형태가 유사하다보니 나중에 헷갈리기 쉬움
암기법 : 수식어 위치. const는 항상 뒷 단어를 수식한다.

  • const 포인터 [변수명] => const는 포인터가 가리키는 값을 상수화. (원본 상수화)
  • 포인터 const [변수명] => const는 변수를 수식. 포인터 변수가 상수화. (포인터 변수 상수화)
  • 이렇게도 쓸 수 있다. 거의 이렇게는 안씀
int const* p = &a;
// 이 경우 const는 *을 수식... 즉, 원본 상수화에 해당함.

이해 확인 문제

int a = 0;
const int* pInt = &a; // 원본 상수화

a = 100;	// 가능할까? 가능

int* p = &a;
*p = 100;	// 가능할까? 가능
  • 포인터 변수 pInt를 통해서 접근했을 때, 값을 바꿀 수 없는것
    • 변수 a, 다른 포인터 변수p를 통한 변경은 여전히 가능함.
    • 즉, pInt 포인터의 기능을 제한한 것.

왜 쓸까?

예시 상황

  1. 어떤 함수의 호출 시, 매개변수가 필요함.
    • 매개변수는 이 함수의 호출 스택에 포함됨.
  2. 근데, 입력받아야하는 데이터가 너무 큰 경우 or 많은 경우.
    그래서 이 게임에서 2개가 존재하지 않는 경우.
    • 차지하는 용량이 너무 커서 2개가 존재하면 메모리가 부족할 경우, 문제가 됨.
    • 함수가 호출될 때, 스택 영역에 이 큰 데이터를 위해서 새로운 공간을 마련함.
    • 이 함수가 자주 호출될수록 비효율은 커짐.
  3. 해결법은 값이 아닌 주소값을 넘겨주면 됨.
  4. 주소값을 넘겨주긴 했는데, 도중에 데이터가 변경될 우려가 있음.
    • 읽기만 할 줄 알았는데, 변경이 일어나 버린것
  • 이러한 상황을 방지하기 위해 const를 사용한 포인터로 넘겨줌
void Output(const something* pI)
{
	// 원본 상수화
    *pI; // 접근 가능
    *pI = value; // 수정 불가
}

int main()
{
	something a = ~~;
    Output(&a);
    
	return 0;
}
  • 근데 외부에서 함수가 주소를 요구하는데, 과연 내가 보내는 값이 멀쩡할까?
    • 함수의 선언 확인법 : ctrl + shift + space
    • 함수 선언에서 const 포인터를 쓴 경우, 읽기만 하겠다는 것.
      • 협업 시, 읽기 전용을 위해선 이렇게 const 포인터를 꼭 붙여주어야 오해가 없음.
    • 물론 이것도 문법적으로 파괴할 순 있음. (절대적이진 않다는 것)
      • 내부에서 새로운 일반 포인터로 캐스팅하는 등으로 바꿀 순 있는 것.
      • 근데, 이건 애초에 잘못 사용하는 것이므로 작성자가 이상한 것임.
    • 단, 위와 같은 경우는 함수 작성자의 의도를 나타낸 것. 읽기 전용으로 쓰겠다.

즉, const 포인터를 쓰는 이유는 주소값을 받을 때
수정하진 않고 읽기용으로 쓰겠다고 알려주기 위해서 사용하는 것.

profile
만성피로 개발자

0개의 댓글