언리얼 엔진/개념 정리

개념 정리 - C++(포인터와 레퍼런스)

journey46110 2026. 7. 9. 15:44

I. 포인터 (Pointer)

1) 포인터의 정의

- 변수의 메모리 주소를 저장하는 변수

int num = 10;
int* ptr = #

ptr에는 num의 주소가 들어 있음

 

 

2) 포인터를 사용하는 이유

① 큰 데이터를 복사하지 않아도 됨

- 값을 복사하면 데이터 크기만큼 복사 비용이 발생함

int       → 4Byte
double    → 8Byte
char      → 1Byte

- int arr[1000000]; 과 같이 큰 배열을 복사하면 매우 비효율적임

- 포인터는 주소 하나(8Byte)만 전달하면 되어서 매우 빠름

 

② 원본 데이터를 직접 수정할 수 있음

int num = 10;
int* ptr = #

*ptr = 20;

결과: num == 20

 

 

3) 주소 연산자 (&)

- 변수의 주소를 얻음

int num = 10;

cout << &num;

결과: num의 주소가 출력됨

 

 

4) 역참조 연산자 (*)

- 포인터가 가리키는 실제 값을 읽거나 수정함

int num = 10;

int* ptr = &num;

cout << *ptr;

*ptr = 30;

cout << *ptr;

결과: 각각 10, 30 출력

 

 

5) 포인터 선언과 초기화

- 포인터 타입은 가리키는 변수의 타입을 의미함

int* p;     //4Byte
char* p;    //1Byte
double* p;  //8Byte

- 포인터 타입을 알고 있어야 주소에서 몇 byte를 읽을 지 알 수 있음

 

int* ptr = nullptr;     //아무 것도 가리키지 않음을 의미함

- 초기화 되지 않은 포인터는 예기치 않은 오류를 일으킬 수 있음

- 반드시 nullptr로 초기화하는 습관이 중요함

 

 

6) 포인터의 크기

- 포인터의 크기는 타입과 관계없이 모두 동일함

sizeof(int*)
sizeof(char*)
sizeof(double*)

- 모두 8Byte (64bit 운영체제 기준, 32bit 운영체제에서는 4Byte)

 

 

7) 배열과 포인터의 유사성

① 배열 이름은 첫 번째 원소의 주소를 의미함

int arr[3];

// arr == &arr[0]

 

② []는 포인터 연산과 같음

arr[2] == *(arr + 2)

arr[i] == *(arr + i)

 

③ 배열 이름은 변경할 수 없음

arr = other; //불가능

- 배열 이름은 상수 포인터처럼 동작함

- 배열 이름이 바뀌게 되면 원소에 임의접근 할 수 없음

 

 

8) 포인터 연산

- 포인터는 주소를 이동함

ptr + 1               //주소 이동

int* -> 4Byte 이동
char* -> 1Byte 이동
double* -> 8Byte 이동

 

 

9) 포인터 배열과 배열 포인터

① 포인터 배열

- 포인터를 여러 개 저장하는 배열

int a = 10, b = 20, c = 30;
int* ptrArr[3] = {&a, &b, &c};

cout << *ptrArr[0] << endl;       //10 출력
cout << *ptrArr[1] << endl;       //20 출력
cout << *ptrArr[2] << endl;       //30 출력

 

메모리 구조

a = 10
b = 20
c = 30
┌──────┐
│      10      │  ← a
└──────┘
┌──────┐
│      20      │  ← b
└──────┘
┌──────┐
│      30      │  ← c
└──────┘

 

ptrArr
┌────────┐
│        &a         │
├────────┤
│         &b        │
├────────┤
│         &c        │
└────────┘

 

② 배열 포인터

- 배열 전체를 가리키는 포인터

int arr[3] = { 100, 200, 300 };
int (*ptr)[3] = &arr; // 배열 포인터 선언

cout << (*ptr)[0] << endl;    //100 출력
cout << (*ptr)[1] << endl;    //200 출력
cout << (*ptr)[2] << endl;    //300 출력

 

메모리 구조

arr
┌──────┬──────┬──────┐
│     100     │     200     │    300     │
└──────┴──────┴──────┘
^
|
ptr

 

- 주로 다차원 배열에서 사용함

int arr[3][4];

int (*ptr)[4] = arr;

 

 

10) 포인터 사용 정리

① 일반 변수를 포인터로 가리킬 때

int a = 5;
int* ptr = &a;

*ptr = 10;        // a == 10

ptr -> 주소
*ptr -> 값

 

② 배열을 포인터로 받을 때

int arr[3] = {1, 2, 3};

int* ptr = arr;       // ptr[i] == *(ptr + i)

arr은 &arr[0]으로 ptr에 주소를 전달하고 있음
ptr[0]은 이미 내부적으로 *가 들어있음 -> *ptr[0]을 하면, *(*(ptr+0))이 되어서 오류가 발생함

 

③ 포인터 배열

int a = 10;
int b = 20;
int c = 30;

int* ptr[3] = {&a, &b, &c};

ptr[0]의 타입은 int*
ptr[i] -> 주소

*ptr[i] -> 값

 

④ 배열 포인터

int arr[3] = {1,2,3};

int (*ptr)[3] = &arr;

ptr은 배열 전체를 가리키는 포인터

*ptr은 arr이 됨 -> (*ptr)[i]는 arr[i]이 됨

 

arr과 &arr의 차이

int arr = {1, 10, 100};

&arr[0] + 1 == 10을 가지는 주소를 가리킴 //int*라면 4byte만큼 이동함
&arr + 1 == 배열 밖의 주소를 가리킴      //int*라면 4byte * 3 해서 12byte만큼 이동함

가리키는 주소는 같지만, 포인터 연산을 할 때 차이가 남

 

 

II. 레퍼런스 (Reference)

1) 레퍼런스의 정의

- 기존 변수의 별명(Alias)

- 선언과 동시에 무조건 초기화해야함

int num = 10;

int& ref = num;

num과 ref는 같음

 

 

2) 값 변경

ref = 30;

cout << num << endl;

30이 출력됨

 

 

3) 포인터와 레퍼런스의 차이

포인터 레퍼런스
주소 저장 변수의 별명
재연결 가능 재연결 불가
nullptr 가능 nullptr 불가
*,& 사용 일반 변수처럼 사용

 

포인터

int num = 10;

int* ptr = &num;

*ptr = 20;

 

레퍼런스

int num = 10;

int& ref = num;

ref = 20;

- 문법이 간단함

 

 

4) 상수 레퍼런스 (const reference)

- 읽기만 가능하고 수정은 불가능함

const int& ref = num;

cout << ref;          // 가능
ref = 30;             // 불가능

복사 비용 없이 데이터를 읽을 때 사용함