Skip to Content
💻 코리아IT아카데미 신촌 - 프로그래밍 학습 자료
C++ 프로그래밍Unit 8: 참조와 함수 고급Topic 1: 참조의 기본 개념

Topic 1: 참조 변수 🔗

🎯 학습 목표

  • 참조 변수의 개념과 특성을 이해할 수 있다
  • 참조 변수의 선언과 초기화 방법을 익힐 수 있다
  • 참조와 포인터의 차이점을 구분할 수 있다
  • 참조를 활용한 효율적인 프로그래밍을 구현할 수 있다

📖 참조(Reference)란?

참조는 이미 존재하는 변수에 대한 별명(alias)입니다. 참조 변수는 원본 변수와 같은 메모리 위치를 공유합니다.

아날로지: 사람의 별명과 같습니다.

  • 원본 변수: 본명 (김철수)
  • 참조 변수: 별명 (철수, 수철이) - 같은 사람을 가리킴
  • 포인터: 주민등록번호 - 그 사람을 찾을 수 있는 정보

🔍 참조 변수 기본 문법

#include <iostream> using namespace std; int main() { int original = 100; // 참조 변수 선언 (반드시 초기화 필요) int& ref = original; // ref는 original의 별명 cout << "=== 참조 변수 기초 ===" << endl; cout << "original 값: " << original << endl; cout << "ref 값: " << ref << endl; cout << "original 주소: " << &original << endl; cout << "ref 주소: " << &ref << endl; // 같은 주소! // 참조를 통한 값 변경 ref = 200; cout << "\nref = 200 후:" << endl; cout << "original 값: " << original << endl; // 200으로 변경됨 cout << "ref 값: " << ref << endl; // 원본을 통한 값 변경 original = 300; cout << "\noriginal = 300 후:" << endl; cout << "original 값: " << original << endl; cout << "ref 값: " << ref << endl; // 300으로 변경됨 return 0; }

참조의 특성

#include <iostream> using namespace std; int main() { cout << "=== 참조의 특성 ===" << endl; // 1. 참조는 반드시 초기화해야 함 int value1 = 10; int& ref1 = value1; // 올바른 초기화 // int& ref2; // 오류! 초기화 없음 // 2. 참조는 재할당 불가능 int value2 = 20; int& ref2 = value2; cout << "초기 상태:" << endl; cout << "ref1 = " << ref1 << ", ref2 = " << ref2 << endl; ref1 = ref2; // ref2의 값(20)을 ref1에 대입 (ref1이 ref2를 참조하는 게 아님) cout << "ref1 = ref2 후:" << endl; cout << "value1 = " << value1 << endl; // 20으로 변경됨 cout << "value2 = " << value2 << endl; // 여전히 20 cout << "ref1 = " << ref1 << endl; // 20 cout << "ref2 = " << ref2 << endl; // 20 // 3. 참조는 null이 될 수 없음 // int& nullRef = nullptr; // 오류! return 0; }

📋 다양한 참조 타입

const 참조

#include <iostream> #include <string> using namespace std; int main() { cout << "=== const 참조 ===" << endl; int number = 42; const int& constRef = number; // const 참조 cout << "number: " << number << endl; cout << "constRef: " << constRef << endl; number = 100; // 원본은 변경 가능 cout << "number 변경 후: " << constRef << endl; // constRef = 200; // 오류! const 참조로는 값 변경 불가 // const 참조는 임시 객체도 참조 가능 const int& tempRef = 500; // 임시값 참조 const string& strRef = "Hello World"; // 문자열 리터럴 참조 cout << "tempRef: " << tempRef << endl; cout << "strRef: " << strRef << endl; return 0; }

배열과 참조

#include <iostream> using namespace std; int main() { cout << "=== 배열과 참조 ===" << endl; int arr[5] = {1, 2, 3, 4, 5}; // 배열 전체에 대한 참조 int (&arrayRef)[5] = arr; cout << "원본 배열: "; for(int i = 0; i < 5; i++) { cout << arr[i] << " "; } cout << endl; // 참조를 통한 배열 수정 arrayRef[2] = 99; cout << "참조로 수정 후: "; for(int i = 0; i < 5; i++) { cout << arr[i] << " "; } cout << endl; // 개별 요소에 대한 참조 int& firstElement = arr[0]; int& lastElement = arr[4]; firstElement = 10; lastElement = 50; cout << "개별 요소 수정 후: "; for(int i = 0; i < 5; i++) { cout << arr[i] << " "; } cout << endl; return 0; }

🔄 함수와 참조

참조는 함수에서 매우 유용하게 사용됩니다.

참조 매개변수

#include <iostream> using namespace std; // 값에 의한 전달 void byValue(int x) { x = 999; cout << "byValue 내부 x: " << x << endl; } // 참조에 의한 전달 void byReference(int& x) { x = 888; cout << "byReference 내부 x: " << x << endl; } // 두 값 교환 함수 void swap(int& a, int& b) { int temp = a; a = b; b = temp; } // 배열 요소 총합 (const 참조 사용) int sumArray(const int (&arr)[5]) { int total = 0; for(int i = 0; i < 5; i++) { total += arr[i]; // arr[i] = 0; // 오류! const 배열 수정 불가 } return total; } int main() { cout << "=== 함수와 참조 매개변수 ===" << endl; int num = 100; cout << "초기 num 값: " << num << endl; byValue(num); cout << "byValue 호출 후 num: " << num << endl; // 100 (변경 안됨) byReference(num); cout << "byReference 호출 후 num: " << num << endl; // 888 (변경됨) // 두 값 교환 cout << "\n--- 값 교환 ---" << endl; int x = 10, y = 20; cout << "교환 전: x=" << x << ", y=" << y << endl; swap(x, y); cout << "교환 후: x=" << x << ", y=" << y << endl; // 배열 합계 cout << "\n--- 배열 합계 ---" << endl; int numbers[5] = {1, 2, 3, 4, 5}; cout << "배열 합계: " << sumArray(numbers) << endl; return 0; }

참조 반환

#include <iostream> using namespace std; // 전역 배열 int numbers[5] = {10, 20, 30, 40, 50}; // 배열 요소 참조 반환 함수 int& getElement(int index) { if(index >= 0 && index < 5) { return numbers[index]; } throw "Index out of range"; } // 배열 출력 함수 void displayArray() { cout << "배열: "; for(int i = 0; i < 5; i++) { cout << numbers[i] << " "; } cout << endl; } // 전역 변수 참조 반환 (안전한 예시) int globalVar = 1000; int& getGlobalVar() { return globalVar; // 안전함 (전역변수) } // 위험한 예시 (사용하지 말 것!) /* int& dangerousFunction() { int localVar = 500; return localVar; // 위험! 지역변수 참조 반환 } */ int main() { cout << "=== 참조 반환 ===" << endl; displayArray(); // 참조 반환을 이용한 값 수정 cout << "\n인덱스 2의 값 변경:" << endl; getElement(2) = 999; displayArray(); // 연쇄 대입도 가능 getElement(0) = getElement(1) = 100; displayArray(); // 전역변수 참조 반환 cout << "\n전역변수 참조:" << endl; cout << "전역변수 값: " << getGlobalVar() << endl; getGlobalVar() = 2000; // 참조를 통한 수정 cout << "수정 후: " << globalVar << endl; return 0; }

🧪 실전 예제: 성적 관리 시스템

#include <iostream> #include <string> using namespace std; // 학생 정보를 위한 전역 변수들 string studentName = "김철수"; int scores[5] = {85, 92, 78, 88, 95}; int scoreCount = 5; // 이름 참조 반환 함수 (수정 가능) string& getName() { return studentName; } // 특정 인덱스의 점수 참조 반환 int& getScore(int index) { if(index >= 0 && index < scoreCount) { return scores[index]; } throw "Score index out of range"; } // 평균 계산 함수 double getAverage() { if(scoreCount == 0) return 0.0; int total = 0; for(int i = 0; i < scoreCount; i++) { total += scores[i]; } return static_cast<double>(total) / scoreCount; } // 성적 정보 출력 함수 void displayGrades() { cout << "\n학생: " << studentName << endl; cout << "점수: "; for(int i = 0; i < scoreCount; i++) { cout << scores[i]; if(i < scoreCount - 1) cout << ", "; } cout << endl; cout << "평균: " << getAverage() << endl; } // 최고 점수 찾기 (참조 반환) int& findMaxScore() { if(scoreCount == 0) { throw "No scores available"; } int maxIndex = 0; for(int i = 1; i < scoreCount; i++) { if(scores[i] > scores[maxIndex]) { maxIndex = i; } } return scores[maxIndex]; } // 성적 향상 함수 void improveGrades(int improvement) { cout << "\n=== 성적 향상 적용 (" << improvement << "점 추가) ===" << endl; for(int i = 0; i < scoreCount; i++) { int& score = getScore(i); // 참조로 받아서 직접 수정 score += improvement; if(score > 100) score = 100; // 최대값 제한 } } int main() { cout << "=== 참조를 활용한 성적 관리 시스템 ===" << endl; // 초기 성적 출력 displayGrades(); // 특정 점수 수정 (참조 사용) cout << "\n3번째 점수를 90점으로 수정" << endl; getScore(2) = 90; // 0-based index displayGrades(); // 최고 점수 찾기 및 보너스 적용 try { int& maxScore = findMaxScore(); cout << "\n최고 점수: " << maxScore << endl; cout << "최고 점수에 5점 보너스 적용" << endl; maxScore += 5; displayGrades(); } catch(const char* error) { cout << "오류: " << error << endl; } // 전체 성적 향상 improveGrades(3); displayGrades(); // 학생 이름 수정 (참조 사용) cout << "\n학생 이름 변경:" << endl; getName() = "김영수"; // 참조를 통한 직접 수정 displayGrades(); return 0; }

🎮 실습 프로젝트: 배열 기반 참조 활용

#include <iostream> #include <string> using namespace std; // 정수 배열 관리 int numbers[10] = {10, 20, 30, 40, 50}; int currentSize = 5; // 배열 요소 참조 반환 (수정 가능) int& getElement(int index) { if(index >= 0 && index < currentSize) { return numbers[index]; } throw "Index out of range"; } // 첫 번째 요소 참조 반환 int& front() { if(currentSize == 0) { throw "Array is empty"; } return numbers[0]; } // 마지막 요소 참조 반환 int& back() { if(currentSize == 0) { throw "Array is empty"; } return numbers[currentSize - 1]; } // 배열 출력 함수 void printArray() { cout << "["; for(int i = 0; i < currentSize; i++) { cout << numbers[i]; if(i < currentSize - 1) cout << ", "; } cout << "]" << endl; } // 배열 요소들의 합계 (참조 사용) int sumArray() { int sum = 0; for(int i = 0; i < currentSize; i++) { sum += numbers[i]; } return sum; } // 모든 요소에 값 더하기 void addToAll(int value) { for(int i = 0; i < currentSize; i++) { int& element = getElement(i); // 참조를 통한 직접 수정 element += value; } } // 문자열 배열 관리 string words[10] = {"Hello", "World", "C++", "Reference"}; int wordCount = 4; // 문자열 요소 참조 반환 string& getWord(int index) { if(index >= 0 && index < wordCount) { return words[index]; } throw "Word index out of range"; } // 문자열 배열 출력 void printWords() { cout << "["; for(int i = 0; i < wordCount; i++) { cout << "\"" << words[i] << "\""; if(i < wordCount - 1) cout << ", "; } cout << "]" << endl; } int main() { cout << "=== 참조를 활용한 배열 관리 ===" << endl; cout << "초기 배열: "; printArray(); // 참조를 통한 요소 수정 cout << "\n=== 개별 요소 수정 ===" << endl; getElement(2) = 99; // 3번째 요소 수정 front() = 5; // 첫 번째 요소 수정 back() = 100; // 마지막 요소 수정 cout << "수정 후: "; printArray(); // 배열 합계 cout << "\n배열 합계: " << sumArray() << endl; // 모든 요소에 값 더하기 cout << "\n모든 요소에 10 더하기" << endl; addToAll(10); printArray(); // 문자열 배열 테스트 cout << "\n=== 문자열 배열 테스트 ===" << endl; cout << "문자열 배열: "; printWords(); // 문자열 수정 getWord(1) = "Beautiful"; getWord(3) += "s"; // 마지막 단어에 's' 추가 cout << "수정 후: "; printWords(); // 예외 처리 테스트 cout << "\n=== 예외 처리 테스트 ===" << endl; try { cout << getElement(100) << endl; // 범위 초과 } catch(const char* error) { cout << "오류 발생: " << error << endl; } return 0; }

💡 참조 vs 포인터 비교

#include <iostream> using namespace std; int main() { cout << "=== 참조 vs 포인터 비교 ===" << endl; int value = 100; // 참조 int& ref = value; // 포인터 int* ptr = &value; cout << "원본 값: " << value << endl; cout << "참조 값: " << ref << endl; cout << "포인터 값: " << *ptr << endl; cout << "\n=== 차이점 정리 ===" << endl; cout << "1. 초기화:" << endl; cout << " 참조: 선언과 동시에 반드시 초기화" << endl; cout << " 포인터: 초기화 선택사항 (nullptr 가능)" << endl; cout << "\n2. 재할당:" << endl; cout << " 참조: 불가능 (평생 같은 변수 참조)" << endl; cout << " 포인터: 가능 (다른 변수 주소로 변경)" << endl; cout << "\n3. 산술 연산:" << endl; cout << " 참조: 불가능" << endl; cout << " 포인터: 가능 (포인터 산술)" << endl; cout << "\n4. 메모리 오버헤드:" << endl; cout << " 참조: 없음 (별명일 뿐)" << endl; cout << " 포인터: 있음 (" << sizeof(ptr) << "바이트)" << endl; cout << "\n5. Null 가능성:" << endl; cout << " 참조: 불가능" << endl; cout << " 포인터: 가능 (nullptr)" << endl; return 0; }

💡 모범 사례와 주의사항

✅ 좋은 습관

  1. 함수 매개변수로 참조 사용

    // 큰 객체의 복사 비용 절약 void processLargeObject(const LargeClass& obj) { // 복사 없이 obj 사용 }
  2. 참조 반환 시 생명주기 확인

    int& getSafeReference() { static int safeValue = 10; // 정적 변수는 안전 return safeValue; }
  3. const 참조 활용

    // 수정하지 않는 경우 const 사용 void printValue(const int& value) { cout << value << endl; }

❌ 피해야 할 실수

  1. 지역변수 참조 반환

    int& dangerous() { int local = 10; return local; // 위험! 댕글링 참조 }
  2. 초기화되지 않은 참조

    // int& ref; // 오류! 참조는 반드시 초기화
  3. 임시 객체에 대한 비const 참조

    // int& ref = 10; // 오류! 임시값은 const 참조만 가능
Last updated on