2026/03/11 TIL
오늘의 목표
- 알고리즘 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 개념 강의 수강