Unreal5 공부/TIL

2026/03/11 TIL

anna59 2026. 3. 11. 21:02

오늘의 목표

  • 알고리즘 2강 수강
  • C++ 문법 1-4 포인터와 레퍼런스 복습
  • C++ 문법 1-5 Class 개념 강의 수강
  • C++ 문법 1-5 Class 개념 복습

추가 목표:

  • C++ 문법 1-6 객체 지향 프로그래밍 강의 수강
  • C++ 문법 1-6 객체 지향 프로그래밍 복습

 

1-5 Class 개념 복습까지는 오늘 꼭 해낼 것!!

 

 

1. 알고리즘 기초 2강

 

vector

 

배울 것

  • vector의 내부 동작 원리
  • 각 연산의 시간 복잡도
  • vector의 기본 사용법(선언, 순회, 수정) 코드로 정리
  • 연속 메모리와 캐시 친화성의 중요도

 

 

 

 

 

 

 

포인터연산과 데이터 크기

#inclulde <iostream>
using namespace std;

int main(){
	int x = 3;
    char y = 'A';
    
    int* ptr1 = &x;
    char* ptr2 = &y;
    
    cout << "ptr1: " << ptr1 << ", ptr1 + 1: " << ptr1 + 1 << endl;
    cout << "ptr2: " << ptr2 << ", ptr2 + 1: " << ptr2 + 1 << endl;
    
    return 0;
}

 

<출력 결과>

ptr1: 000000F6D4FBF884, ptr1 + 1: 000000F6D4FBF888
ptr2: 000000F6D4FBF8A4, ptr2 + 1: 000000F6D4FBF8A5

 

 

ptr1은 int pointer => 4바이트 차이

ptr2는 char pointer => 1바이트 차이 

 

 

포인터를 이용한 값 읽기

#include <iostream>
using namespace std;

int main() {
	int a = 10;
    int* p = &a;	// 변수 a의 주소를 저장
    
    cout << "포인터 p가 가리키는 값: " << *p << endl;	//p가 가리키는 변수 a의 값
    
    return 0;
}

 

<예측 결과>

포인터 p가 가리키는 값: 10

 

<출력 결과>

포인터 p가 가리키는 값: 10

 

 

포인터를 이용한 값 변경

#include <iostream>
using namespace std;

int main() {
	int a = 10;
    int* p = &a;	// a의 주소 저장
    
    cout << "변경 전 a: " << a << endl;
    
    *p = 20;	// 포인터로 값 변경
    
    cout <<  "변경 후 a: " << a << endl;
    
    return 0;

}

 

<예측 결과>

변경 전 a: 10
변경 후 a: 20

 

<출력 결과>

변경 전 a: 10
변경 후 a: 20

 

 

포인터 변수의 역참조

포인터와 변수의 관계

#include <iostream>
using namespace std;

int main() {
	int x = 3;	// 정수형 변수 x 선언
    int* ptr = &x;	// 포인터 ptr에 x의 주소 저장
    
    cout << "x의 값: " << x << endl;
    cout << "x의 주소: " << &x << endl;
    cout << "ptr의 값(저장된 주소): " << ptr << endl;
    cout << "*ptr이 가리키는 값: " << *ptr << endl;
    
    return 0;
}

 

<예측 결과>

x의 값: 3
x의 주소: (x의 주소값)
ptr의 값(저장된 주소): (x의 주소값)
*ptr이 가리키는 값: 3

 

<출력 결과>

x의 값: 3
x의 주소: 00000006DF6FFB84
ptr의 값(저장된 주소): 00000006DF6FFB84
*ptr이 가리키는 값 3

 

다른 타입의 변수를 가리키려 할 때 발생하는 문제

#include <iostream>
using namespace std;

int main() {
	int x = 3;
    char y = 'A';
    
    int* ptr = &x; // 정상적인 포인터 할당
    // ptr = &y; 오류 발생: char형 변수를 int* 포인터에 저장 불가능
    
    return 0;
}

 

각각 올바른 포인터 타입을 사용하는 포인터

 

#include <iostream>
using namespace std;

int main(){
	int x = 3;
    char y = 'A';
    
    int* intPtr = &x; // 정수형 포인터
    char* charPtr = &y; // 문자형 포인터
    
    cout << "*intPtr: " << *intPtr << endl;
    cout << "*charPtr: " << *charPtr << endl;
    
    return 0;
}

 

<예측 결과>

*intPtr: 3
*charPtr: A

 

<출력 결과>

*intPtr: 3
*charPtr: A

 

 

포인터 연산

#include <iostream>
using namespace std;

int main() {
	int x = 3;
    char y = 'A';
    
    int* ptr1 = &x;
    char* ptr2 = &y;
    
    cout << "ptr1: " << ptr1 << ", ptr1 + 1: " << ptr + 1 << endl;
    cout << "ptr2: " << (void*)ptr2 << ", ptr2 + 1: " << (void*)(ptr + 2) << endl;
    
    return 0;
}

 

<예측 결과>

ptr1: (x의 주소값), ptr1 + 1: (x의 주소값 + 4)
ptr2: (y의 주소값), ptr2 + 1: (y의 주소값 + 1)

 

<출력 결과>

ptr1: 00000019D96FFBA4, ptr1 + 1: 00000019D96FFBA8
ptr2: 00000019D96FFBC4, ptr2 + 1: 00000019D96FFBC5

 

 

잘못된 포인터 접근

#include <iostream>
using namespace std;

int main(){
	int* p; // 초기화되지 않은 포인터
    
    //*p = 10; // 실행 시 오류 발생 가능: 메모리 접근 오류
    
    return 0;
}

 

 

배열의 기본 개념과 인덱스를 통한 접근

#include <iostream>
using namespace std;

// 배열의 선언, 초기화 및 인덱스를 통한 접근 예제
int main(){
	int arr[5] = {10, 20, 30, 40, 50}; // 크기가 5인 배열 선언 및 초기화
    
    // 배열 요소 접근 (인덱스를 이용한 접근)
    cout << "arr[0]: " << arr[0] << endl; // 10
    cout << "arr[1]: " << arr[1] << endl; // 30
    cout << "arr[2]: " << arr[2] << endl; // 50
    
    cout << "배열의 시작 주소: " << arr << endl;
    cout << "첫 번째 요소의 주소: " << &arr[0] << endl;
    
    return 0;
}

 

<예측 결과>

arr[0]: 10
arr[2]: 30
arr[4]: 50
배열의 시작 주소: (arr 배열 시작 주소)
첫 번째 요소의 주소: (arr 배열 시작 주소)

 

<출력 결과>

arr[0]: 10
arr[2]: 30
arr[4]: 50
배열의 시작 주소: 0000003BEFFBFCC8
첫 번째 요소의 주소: 0000003BEFFBFCC8

 

 

포인터를 이용한 배열 접근

 

#include <iostream>
using namespace std;

// 배열 이름을 포인터처럼 활용하는 예제
int main() {
	int arr[3] = {100, 200, 300};
    int *ptr = arr; // 배열 이름은 배열의 시작 주소를 기리킴
    
    cout << "*ptr: " << *ptr << endl;	// 100 (arr[0] 값)
    cout << "*(ptr+1): " << *(ptr+1) << endl;	// 200 (arr[1] 값)
    cout << "*(ptr+2): " << *(ptr+2) << endl;	// 300 (arr[2] 값)
    
    return 0;
}

 

<예측 결과>

*ptr: 100
*(ptr+1): 200
*(ptr+2): 300

 

<출력 결과>

*ptr: 100
*(ptr + 1): 200
*(ptr + 2): 300

 

 

배열 이름을 변경할 수 없는 이유

#include <iostream>
using namespace std;

// 배열 이름을 변경할 수 없는 이유를 보여주는 예제
int main() {
	int arr[3] = {1, 2, 3};
    int *ptr = arr;	// 정상: 포인터 변수에 배열의 시작 주소 저장
    
    // arr 자체를 변경하려고 시도 (컴파일 오류 발생
    // arr = ptr + 1; // 배열 이름은 주소 상수를 저장하므로 변경 불가!
    
    cout << "배열 시작 주소: " << arr << endl;
    cout << "포인터 값: " << ptr << endl;
    
    return 0;
}

 

<출력 결과>

오류 메세지: 배열 형식 'int [3]'은(는) 할당할 수 없습니다.

 

 

포인터 배열과 배열 포인터 

포인터 배열: 포인터를 원소로 갖는 배열

배열 포인터: 배열 전체를 가리키는 포인터 (주로 다차원 배열 제어에 사용)

 

포인터 배열

 

#include <iostream>
using namespace std;

// 포인터 배열: 포인터를 원소로 갖는 배열
int main() {
	int a = 10, b = 20, c = 30;
    int* ptrArr[3] = { &a, &b, &c }; // 포인터 배열 선언 및 초기화
    
    // 포인터 배열을 이용하여 값 출력
    cout << "*ptrArr[0]: " << *ptrArr[0] << endl; // 10
    cout << "*ptrArr[1]: " << *ptrArr[1] << endl; // 20
    cout << "*ptrArr[2]: " << *ptrArr[2] << endl; // 30
    
    return 0;
}

<예측 결과>

*ptrArr[0]: 10
*ptrArr[1]: 20
*ptrArr[2]: 30

 

<출력 결과>

*ptrArr[0]: 10
*ptrArr[1]: 20
*ptrArr[2]: 30

 

배열 포인터

#include <iostream>
using namespace std;

int main() {
	int arr[3] = {100, 200, 300};
    int (*ptr)[3] = &arr; // 배열 포인터 선언
    
    cout << "(*ptr)[0]: " << (*ptr)[0] << endl;
    cout << "(*ptr)[1]: " << (*ptr)[1] << endl;
    cout << "(*ptr)[2]: " << (*ptr)[2] << endl;
    
    return 0;
}

 

<예측 결과>

(*ptr)[0]: 100
(*ptr)[1]: 200
(*ptr)[2]: 300

 

<출력 결과>

(*ptr)[0]: 100
(*ptr)[1]: 200
(*ptr)[2]: 300

 

<개인 노트> 

포인터 배열과 배열 포인터의 차이

포인터 배열: 배열의 인자가 포인터이기 때문에 값을 사용할 때 *(ptr[0]) 사용

배열 포인터: 배열 전체가 포인터이기 때문에 값을 사용할 때 (*ptr)[0] 사용

 

 

 

포인터 배열 vs 배열 포인터

#include <iostream>
using namespace std;

// 포인터 배열과 배열 포인터의 차이점 확인
int main() {
	int x = 1, y = 2, z = 3;
    int* ptrArr[3] = {&x, &y, &z};	// 포인터 배열 (각 원소가 int* 타입)
    
    int arr[3] = {10, 20, 30};
    int (*ptr)[3] = &arr;	// 배열 포인터 (배열 전체를 가리킴)
    
    // 포인터 배열을 통한 접근
    cout << "(*ptr)[0]: " << (*ptr)[0] << endl;
    cout << "(*ptr)[1]: " << (*ptr)[1] << endl;
    cout << "(*ptr)[2]: " << (*ptr)[2] << endl;
    
    return 0;
    
}

 

<예측 결과>

*ptrArr[0]: 1
*ptrArr[1]: 2
*ptrArr[2]: 3
*(ptr)[0]: 1
*(ptr)[1]: 2
*(ptr)[2]: 3

 

<출력 결과>

*ptrArr[0]: 1
*ptrArr[1]: 2
*ptrArr[2]: 1
(*ptr)[0]: 1
(*ptr)[1]: 2
(*ptr)[2]: 3

 

배열 포인터의 잘못된 예시

#include <iostream>
using namespace std;

// 배열 포인터의 잘못된 사용 예제
int main() {
    int arr[3] = {10, 20, 30};
    int (*ptr)[3] = &arr; // 배열 포인터 선언

    // 배열 포인터는 단일 요소를 직접 가리킬 수 없음
    // ptr = arr; // 오류 발생: 배열의 첫 번째 요소 주소와 다름

    return 0;
}

/*
컴파일 오류 메시지 예시:
error: invalid conversion from 'int*' to 'int (*)[3]'
*/

 

레퍼런스

일반 변수와 거의 동일하게 사용 가능

내부적으로는 해당 변수를 직접 가리켜 주는 역할 (별명 부여)

 

기본적인 레퍼런스 사용

#include <iostream>
using namespace std;

// 레퍼런스를 활용하여 변수에 별명을 부여하는 예제
int main() {
	int var = 10;
    int& ref = var; // var의 레퍼런스 선언
    
    cout << "초기 값:" << endl;
    cout << "var: " << var << endl; // 10
    cout << "ref: " << ref << endl; // 10
    
    ref = 20; // ref를 변경하면 var도 변경됨
    
    cout << "ref 값을 변경한 후:" << endl;
    cout << "var: " << var << endl; // 20
    cout << "ref: " << ref << endl; // 20
     
    return 0;
}

 

<출력 결과>

초기 값:
var: 10
ref: 10
ref 값을 변경한 후:
var: 20
ref: 20

 

<?> var 값을 변경했을 때 ref 값이 변할까?

추측: 변할 것 같다

 

<예제 코드 작성>

#include <iostream>
using namespace std;

// 레퍼런스를 활용하여 변수에 별명을 부여하는 예제
int main() {
    int var = 10;
    int& ref = var; // var의 레퍼런스 선언

    cout << "초기 값:" << endl;
    cout << "var: " << var << endl; // 10
    cout << "ref: " << ref << endl; // 10

    ref = 20; // ref를 변경하면 var도 변경됨

    cout << "ref 값을 변경한 후:" << endl;
    cout << "var: " << var << endl; // 20
    cout << "ref: " << ref << endl; // 20

    var = 30;

    cout << "var 값을 변경한 후:" << endl;
    cout << "var: " << var << endl;
    cout << "ref: " << ref << endl;

    return 0;
}

 

<출력 결과>

초기 값:
var: 10
ref: 10
ref 값을 변경한 후:
var: 20
ref: 20
var 값을 변경한 후:
var: 30
ref: 30

 

var 값을 바꿨을 때 ref의 값도 바뀌는 것을 확인.

 

 

 

잘못된 레퍼런스 사용

#include <iostream>
using namespace std;

// 잘못된 레퍼런스 선언 예제
int main(){
	int& ref; // 레퍼런스는 선언과 동시에 초기화해야 함
    
    return 0;
}

/*
컴파일 오류 메세지 예시:
error: 'ref' declared as reference but not initialized
*/

 

<오류 메세지>

'ref': 참조를 초기화해야 합니다.

 

 

포인터와 레퍼런스의 차이점

 

포인터: Null / nullptr 가질 수 있음

레퍼런스: 항상 다른 변수와 연결되어 있기 때문에 Null 불가능

포인터에서 주소를 가져올 때 사용하는 &연산과는 구별.  일반 변수와 연산 방법이 같음.

 

 

선언 및 초기화 차이

#include <iostream>
using namespace std;

// 포인터와 레퍼런스의 선언 및 초기화 차이
int main() {
	int a = 10, b = 20;
    
    // 포인터는 선언 후 나중에 다른 변수를 가리킬 수 있음
    int* ptr = &a; // 포인터 선언 및 초기화
    ptr = &b; // 포인터가 다른 변수를 가리킬 수 있음
    
    // 레퍼런스는 선언과 동시에 초기화해야 함
    int& ref = a;
    // ref = &b; // 오류! 레퍼런스는 다른 변수에 재할당할 수 없음
    
    cout << "포인터 사용:" << endl;
    cout << "*ptr: " << *ptr << endl; // 20 (포인터가 b를 가리키고 있음)
    
    cout << "레퍼런스 사용:" << endl;
    cout << "ref: " << ref << endl; // 10 (a를 가리키고 있음)
    
    return 0;
}

 

<출력 결과>

포인터 사용:
*ptr: 20
레퍼런스 사용:
ref: 10

 

 

 

nullptr가능성 여부의 차이

#include <iostream>
using namespace std;

// 포인터는 Null을 가질 수 있지만, 레퍼런스는 반드시 변수와 연결되어야 함
int main() {
	int a = 42
    int* ptr = nullptr; // 포인터는 nullptr이 가능
    ptr = &a; // 이후에 a를 가리키도록 설정 가능
    
    // int& ref; // 오류! 레퍼런스는 반드시 선언과 동시에 초기화해야 함
    int& ref = a; // 올바른 선언 방식
    
    cout << "포인터 사용:" << endl;
    cout << "ptr이 가리키는 값: " << *ptr << endl; // 42
    
    cout << "레퍼런스 사용: " << endl;
    cout << "ref: " << ref << endl; // 42
    
    return 0;
}

 

<예측 결과>

포인터 사용:
ptr이 가리키는 값: 42
레퍼런스 사용:
ref: 42

 

<출력 결과>

포인터 사용:
ptr이 가리키는 값: 42
레퍼런스 사용:
ref: 42

 

 

 

포인터와 레퍼런스의 간접 참조 문법 차이

#include <iostream>
using namespace std;

int main(){
	int x = 5;
    int* ptr = &x;	// 포인터 선언
    int& ref = x;	// 레퍼런스 선언
    
    cout << "포인터 접근 방법:" << endl;
    cout << "x: " << x << endl;	// 5
    cout << "*ptr: " << *ptr << endl;	// 5 (포인터를 통한 간접 참조)
    cout << "ptr: " << ptr << endl;	// x의 주소값
    
    cout << "레퍼런스 접근 방법: << endl;
    cout << "ref: " << ref << endl;	// 5 (레퍼런스는 그냥 변수처럼 사용 가능)
    
    *ptr = 10;	// 포인터를 사용하여 값 변경
    cout << "포인터로 변경 후 x: " << x << endl;	// 10
    
    ref = 20;	// 레퍼런스로 값 변경
    cout << "레퍼런스로 변경 후 x: " << x << endl;	// 10
    
    return 0;
}

 

<출력 결과>

포인터 접근 방법:
x: 5
*ptr: 5
ptr: 000000B6C08FF564
레퍼런스 접근 방법:
ref: 5
포인터로 변경 후 x: 10
레퍼런스로 변경 후 x: 20

 

 

 

잘못된 레퍼런스 사용 예시

#include <iostream>
using namespace std;

int main(){
	int* ptr = nullptr;	// 포인터는 nullptr 가능
    int& ref = nullptr;	// 오류 발생: 레퍼런스는 NULL을 가질 수 없음
	
    return 0;
}

/*
컴파일 오류 메세지 예시:
error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'std::nullptr_t'
*/

 

 

상수 레퍼런스

 

상수 레퍼런스의 기본 개념

 

#include <iostream>
using namespace std;

int main() {
	int x = 100;
    const int& cref = x;	// x를 읽기 전용으로 참조
    
    cout << "cref: " << cref << endl;	// 100
    
    // cref = 200;	// 오류 발생! 상수 레퍼런스는 값을 변경할 수 없음
    
    x = 200;
    cout << "x 변경 후 cref: " << cref << endl; // 200
    
    return 0;
    
}

 

<출력 결과>

 

상수 레퍼런스를 잘못 사용하는 경우

include <iostream>
using namespace std;

// 잘못된 상수 레퍼런스 사용 예제
int main(){
	const int& cref; // 오류 발생! 레퍼런스는 반드시 초기화해야 함
    
    return;
}

/*
컴파일 오류 메시지 예시:
error: 'cref' declared as reference but not initialized
*/

 

 

숙제 1. 포인터 변수를 활용한 변수값 변경

 

아래 제공된 코드를 활용해서 정수형 변수를 선언하고, 그 변수를 가리키는 포인터를 만든 뒤, 포인터를 사용하여 변수의 값을 출력하려 합니다. 코드의 ___ 으로 표시된 빈칸을 적절한 C++ 코드로 채워서 프로그램을 완성하세요.

 

#include <iostream>

int main() {
    // 1. 정수형 변수 value를 선언하고 25라는 값을 할당하세요.
    int value = ___; // <-- 이 부분을 채우세요. value 변수에 초기값 25를 넣으세요.

    // 2. value 변수의 주소를 저장할 포인터 변수 ptr을 선언하세요.
    //    ptr이 value를 가리키도록 value 변수의 주소를 ptr에 저장하세요.
    int* ptr = ___; // <-- 이 부분을 채우세요. value 변수의 주소를 여기에 넣으세요.

    // 3. 포인터 ptr을 사용하여 value 변수의 값을 출력하세요.
    //    (힌트: 포인터가 가리키는 값을 얻으려면 역참조 연산자 '*'를 사용합니다.)
    std::cout << "포인터를 통해 접근한 value의 값: " << ___; // <-- 이 부분을 채우세요. ptr이 가리키는 값을 출력하세요.
    std::cout << std::endl;

    return 0;
}

 

답안

#include <iostream>

int main() {
    // 1. 정수형 변수 value를 선언하고 25라는 값을 할당하세요.
    int value = 25; // <-- 이 부분을 채우세요. value 변수에 초기값 25를 넣으세요.

    // 2. value 변수의 주소를 저장할 포인터 변수 ptr을 선언하세요.
    //    ptr이 value를 가리키도록 value 변수의 주소를 ptr에 저장하세요.
    int* ptr = &value; // <-- 이 부분을 채우세요. value 변수의 주소를 여기에 넣으세요.

    // 3. 포인터 ptr을 사용하여 value 변수의 값을 출력하세요.
    //    (힌트: 포인터가 가리키는 값을 얻으려면 역참조 연산자 '*'를 사용합니다.)
    std::cout << "포인터를 통해 접근한 value의 값: " << *ptr; // <-- 이 부분을 채우세요. ptr이 가리키는 값을 출력하세요.
    std::cout << std::endl;

    return 0;
}

 

정답!

 

숙제 2. 레퍼런스 변수를 활용한 변수값 변경

 

아래 C++ 코드는 정수형 변수를 선언하고, 그 변수에 대한 레퍼런스를 생성한 뒤, 레퍼런스를 사용하여 변수의 값을 출력하고 변경하는 과정을 보여줍니다. 코드의___으로 표시된 빈칸을 적절한 C++ 코드로 채워서 프로그램을 완성하세요.

 

#include <iostream>

int main() {
    // 1. 정수형 변수 score를 선언하고 80이라는 값을 할당하세요.
    int score = ___; // <-- 이 부분을 채우세요. score 변수에 초기값 80을 넣으세요.

    // 2. score 변수를 참조(Reference)하는 레퍼런스 변수 score_ref를 선언하세요.
    int& score_ref = ___; // <-- 이 부분을 채우세요. score_ref가 score를 참조하게 만드세요.

    // 3. 레퍼런스 score_ref를 사용하여 score 변수의 값을 출력하세요.
    std::cout << "레퍼런스를 통해 접근한 score의 값: " << ___; // <-- 이 부분을 채우세요. score_ref를 사용하세요.
    std::cout << std::endl;

    // 4. 레퍼런스 score_ref를 사용하여 score 변수의 값을 95로 변경하세요.
    score_ref = ___; // <-- 이 부분을 채우세요. score_ref에 새 값 95를 할당하세요.

    // 5. score 변수의 값이 실제로 변경되었는지 확인하기 위해 score 변수 자체를 출력하세요.
    std::cout << "레퍼런스 변경 후 score의 값: " << ___; // <-- 이 부분을 채우세요. score 변수 자체를 사용하세요.
    std::cout << std::endl;


    return 0;
}

 

답안

#include <iostream>

int main() {
    // 1. 정수형 변수 score를 선언하고 80이라는 값을 할당하세요.
    int score = 80; // <-- 이 부분을 채우세요. score 변수에 초기값 80을 넣으세요.

    // 2. score 변수를 참조(Reference)하는 레퍼런스 변수 score_ref를 선언하세요.
    int& score_ref = &score; // <-- 이 부분을 채우세요. score_ref가 score를 참조하게 만드세요.

    // 3. 레퍼런스 score_ref를 사용하여 score 변수의 값을 출력하세요.
    std::cout << "레퍼런스를 통해 접근한 score의 값: " << score_ref; // <-- 이 부분을 채우세요. score_ref를 사용하세요.
    std::cout << std::endl;

    // 4. 레퍼런스 score_ref를 사용하여 score 변수의 값을 95로 변경하세요.
    score_ref = 95; // <-- 이 부분을 채우세요. score_ref에 새 값 95를 할당하세요.

    // 5. score 변수의 값이 실제로 변경되었는지 확인하기 위해 score 변수 자체를 출력하세요.
    std::cout << "레퍼런스 변경 후 score의 값: " << score; // <-- 이 부분을 채우세요. score 변수 자체를 사용하세요.
    std::cout << std::endl;


    return 0;
 
}

 

레퍼런스 초기화 시 주소값이 아닌 변수 그 자체를 대입하면 됨!!

int& score_ref = &score; (x)

int& score_ref = score; (O)

 

 

3. C++ 문법 1-5 Class 개념 강의 수강