Skip to Content
💻 코리아IT아카데미 신촌 - 프로그래밍 학습 자료
C++ 프로그래밍Unit 5: 함수 기초Topic 4: 변수의 범위와 생명주기

Topic 4: 변수의 범위와 생명주기 🌍

🎯 학습 목표

  • 변수의 스코프와 생명주기 개념을 이해할 수 있다
  • 지역변수, 전역변수, 정적변수의 차이점을 설명할 수 있다
  • 변수 선언 위치에 따른 영향을 파악하고 적절히 활용할 수 있다

🌟 변수의 세계: 범위와 생명주기

“변수들은 각자의 영역에서 살아가며, 태어나고 사라지는 시간이 정해져 있어요!”

변수의 **범위(Scope)**와 **생명주기(Lifetime)**를 이해하는 것은 올바른 프로그램을 만드는 핵심입니다.

🏠 아파트 비유:

  • 각 방(함수)에 있는 물건(지역변수) → 그 방에서만 사용 가능
  • 공용 공간(전역) 물건(전역변수) → 모든 방에서 사용 가능
  • 관리실 장부(정적변수) → 한 번 만들어지면 계속 보관

🏠 지역변수 (Local Variable)

1. 기본 지역변수

#include <iostream> using namespace std; void testFunction() { int localVar = 10; // 지역변수 - 이 함수에서만 사용 가능 cout << "함수 내부 localVar: " << localVar << endl; // 여기서 localVar는 유효함 localVar = 20; cout << "변경된 localVar: " << localVar << endl; } // 여기서 localVar는 소멸됨 int main() { testFunction(); // cout << localVar << endl; // 오류! localVar는 여기서 접근 불가 return 0; }

출력:

함수 내부 localVar: 10 변경된 localVar: 20

2. 블록 스코프 (Block Scope)

#include <iostream> using namespace std; int main() { int outerVar = 100; // 바깥쪽 변수 cout << "바깥쪽 outerVar: " << outerVar << endl; { // 새로운 블록 시작 int innerVar = 200; // 블록 내부 변수 cout << "블록 내부 innerVar: " << innerVar << endl; cout << "블록 내부에서 outerVar: " << outerVar << endl; // 접근 가능 outerVar = 150; // 바깥쪽 변수 수정 가능 } // innerVar는 여기서 소멸 cout << "수정된 outerVar: " << outerVar << endl; // cout << innerVar << endl; // 오류! innerVar는 접근 불가 return 0; }

출력:

바깥쪽 outerVar: 100 블록 내부 innerVar: 200 블록 내부에서 outerVar: 100 수정된 outerVar: 150

3. 반복문과 조건문에서의 스코프

#include <iostream> using namespace std; int main() { // for 루프의 변수 스코프 for (int i = 0; i < 3; i++) { int loopVar = i * 10; // 매번 새로 생성됨 cout << "반복 " << i << ": loopVar = " << loopVar << endl; } // cout << i << endl; // 오류! i는 for 루프에서만 유효 // cout << loopVar << endl; // 오류! loopVar도 접근 불가 // if 문의 변수 스코프 bool condition = true; if (condition) { int ifVar = 42; cout << "if 블록 내부 ifVar: " << ifVar << endl; if (ifVar > 40) { int nestedVar = 100; // 중첩 블록의 변수 cout << "중첩 블록 nestedVar: " << nestedVar << endl; } // cout << nestedVar << endl; // 오류! 중첩 블록에서만 유효 } // cout << ifVar << endl; // 오류! if 블록에서만 유효 return 0; }

🌍 전역변수 (Global Variable)

1. 기본 전역변수

#include <iostream> using namespace std; // 전역변수 선언 - 모든 함수에서 접근 가능 int globalCounter = 0; string globalMessage = "Hello Global!"; void incrementCounter() { globalCounter++; // 전역변수 수정 cout << "함수에서 globalCounter: " << globalCounter << endl; } void displayMessage() { cout << "전역 메시지: " << globalMessage << endl; } int main() { cout << "초기 globalCounter: " << globalCounter << endl; incrementCounter(); incrementCounter(); globalCounter += 10; // main에서도 접근 가능 cout << "main에서 globalCounter: " << globalCounter << endl; displayMessage(); return 0; }

출력:

초기 globalCounter: 0 함수에서 globalCounter: 1 함수에서 globalCounter: 2 main에서 globalCounter: 12 전역 메시지: Hello Global!

2. 전역변수와 지역변수 이름이 같은 경우

#include <iostream> using namespace std; int number = 100; // 전역변수 void testSameName() { int number = 200; // 지역변수 (전역변수를 가림) cout << "함수 내부 number: " << number << endl; // 지역변수 출력 cout << "전역 number: " << ::number << endl; // ::로 전역변수 접근 } int main() { cout << "main의 전역 number: " << number << endl; testSameName(); { int number = 300; // 또 다른 지역변수 cout << "블록 내부 number: " << number << endl; cout << "전역 number: " << ::number << endl; } return 0; }

출력:

main의 전역 number: 100 함수 내부 number: 200 전역 number: 100 블록 내부 number: 300 전역 number: 100

⚡ 정적변수 (Static Variable)

1. 정적 지역변수

#include <iostream> using namespace std; void countCalls() { static int callCount = 0; // 정적 지역변수 - 한 번만 초기화 callCount++; cout << "이 함수는 " << callCount << "번 호출되었습니다." << endl; } void normalFunction() { int normalCount = 0; // 일반 지역변수 - 매번 초기화 normalCount++; cout << "일반 변수 값: " << normalCount << endl; } int main() { cout << "=== 정적변수 테스트 ===" << endl; countCalls(); countCalls(); countCalls(); cout << "\n=== 일반변수 테스트 ===" << endl; normalFunction(); normalFunction(); normalFunction(); return 0; }

출력:

=== 정적변수 테스트 === 이 함수는 1번 호출되었습니다. 이 함수는 2번 호출되었습니다. 이 함수는 3번 호출되었습니다. === 일반변수 테스트 === 일반 변수 값: 1 일반 변수 값: 1 일반 변수 값: 1

2. 정적변수를 활용한 실전 예제

#include <iostream> #include <string> using namespace std; // ID 생성기 int generateID() { static int nextID = 1000; // 정적변수로 ID 관리 return nextID++; } // 사용자 생성 함수 string createUser(const string& name) { static int userCount = 0; // 생성된 사용자 수 카운트 userCount++; int userID = generateID(); cout << "[사용자 생성] 총 " << userCount << "명의 사용자가 생성되었습니다." << endl; return "User" + to_string(userID) + "_" + name; } int main() { cout << "=== 사용자 생성 시스템 ===" << endl; string user1 = createUser("김철수"); cout << "생성된 사용자: " << user1 << endl << endl; string user2 = createUser("이영희"); cout << "생성된 사용자: " << user2 << endl << endl; string user3 = createUser("박민수"); cout << "생성된 사용자: " << user3 << endl << endl; cout << "추가 ID 생성: " << generateID() << endl; cout << "추가 ID 생성: " << generateID() << endl; return 0; }

출력:

=== 사용자 생성 시스템 === [사용자 생성] 총 1명의 사용자가 생성되었습니다. 생성된 사용자: User1000_김철수 [사용자 생성] 총 2명의 사용자가 생성되었습니다. 생성된 사용자: User1001_이영희 [사용자 생성] 총 3명의 사용자가 생성되었습니다. 생성된 사용자: User1002_박민수 추가 ID 생성: 1003 추가 ID 생성: 1004

🔄 변수의 생명주기 (Lifetime)

1. 생명주기 비교

#include <iostream> using namespace std; int globalVar = 1; // 프로그램 시작부터 끝까지 생존 void demonstrateLifetime() { static int staticVar = 10; // 첫 호출 시 생성, 프로그램 끝까지 생존 int localVar = 100; // 함수 호출 시 생성, 함수 끝에서 소멸 cout << "=== 함수 내부 ===" << endl; cout << "전역변수: " << globalVar++ << endl; cout << "정적변수: " << staticVar++ << endl; cout << "지역변수: " << localVar++ << endl; { int blockVar = 1000; // 블록 진입 시 생성, 블록 끝에서 소멸 cout << "블록변수: " << blockVar++ << endl; } // blockVar는 여기서 소멸됨 } int main() { cout << "프로그램 시작 - 전역변수: " << globalVar << endl; cout << "\n첫 번째 호출:" << endl; demonstrateLifetime(); cout << "\n두 번째 호출:" << endl; demonstrateLifetime(); cout << "\n세 번째 호출:" << endl; demonstrateLifetime(); cout << "\n프로그램 끝 - 전역변수: " << globalVar << endl; return 0; }

출력:

프로그램 시작 - 전역변수: 1 첫 번째 호출: === 함수 내부 === 전역변수: 1 정적변수: 10 지역변수: 100 블록변수: 1000 두 번째 호출: === 함수 내부 === 전역변수: 2 정적변수: 11 지역변수: 100 블록변수: 1000 세 번째 호출: === 함수 내부 === 전역변수: 3 정적변수: 12 지역변수: 100 블록변수: 1000 프로그램 끝 - 전역변수: 4

🎮 실전 프로젝트: 간단한 점수 카운터

정적변수를 활용한 점수 시스템

#include <iostream> using namespace std; void addScore(int points) { static int totalScore = 0; // 정적변수 - 점수 누적 static int gameCount = 0; // 정적변수 - 게임 횟수 totalScore += points; gameCount++; cout << "게임 " << gameCount << "회: " << points << "점 획득!" << endl; cout << "누적 점수: " << totalScore << "점" << endl; cout << "평균 점수: " << (totalScore / gameCount) << "점" << endl; cout << "------------------------" << endl; } int main() { cout << "=== 점수 카운터 시스템 ===" << endl; // 여러 번 점수 추가 addScore(100); addScore(150); addScore(80); addScore(200); return 0; }

출력:

=== 점수 카운터 시스템 === 게임 1회: 100점 획득! 누적 점수: 100점 평균 점수: 100점 ------------------------ 게임 2회: 150점 획득! 누적 점수: 250점 평균 점수: 125점 ------------------------ 게임 3회: 80점 획득! 누적 점수: 330점 평균 점수: 110점 ------------------------ 게임 4회: 200점 획득! 누적 점수: 530점 평균 점수: 132점 ------------------------

🔧 변수 범위 문제 해결하기

1. 변수 이름 충돌 해결

#include <iostream> using namespace std; int value = 100; // 전역변수 void demonstrateScopes() { cout << "=== 스코프 해결 방법들 ===" << endl; // 방법 1: :: 연산자로 전역변수 접근 int value = 200; // 지역변수 cout << "지역변수 value: " << value << endl; cout << "전역변수 value: " << ::value << endl; // 방법 2: 다른 이름 사용 int localValue = 300; int globalValue = ::value; cout << "명확한 이름 - localValue: " << localValue << endl; cout << "명확한 이름 - globalValue: " << globalValue << endl; // 방법 3: 블록으로 스코프 분리 { int blockValue = 400; cout << "블록 내부 blockValue: " << blockValue << endl; // 여기서는 바깥쪽 value들에 접근 가능 cout << "블록에서 지역변수: " << value << endl; cout << "블록에서 전역변수: " << ::value << endl; } } int main() { demonstrateScopes(); return 0; }

2. 함수 매개변수와 지역변수

#include <iostream> using namespace std; int globalX = 10; // 매개변수 이름이 전역변수와 같은 경우 void processValue(int globalX) { // 매개변수가 전역변수를 가림 int localY = 20; cout << "매개변수 globalX: " << globalX << endl; // 매개변수 값 cout << "실제 전역 globalX: " << ::globalX << endl; // 전역변수 값 cout << "지역변수 localY: " << localY << endl; // 매개변수 수정 (원본에는 영향 없음) globalX = 999; cout << "수정된 매개변수: " << globalX << endl; cout << "전역변수는 그대로: " << ::globalX << endl; } void processReference(int& refX, int& refY) { // 참조로 받기 cout << "\n참조 매개변수:" << endl; cout << "참조 refX: " << refX << endl; cout << "참조 refY: " << refY << endl; refX = 777; // 원본 수정됨 refY = 888; } int main() { cout << "처음 전역 globalX: " << globalX << endl; processValue(globalX); cout << "함수 호출 후 전역 globalX: " << globalX << endl; int localVar = 50; processReference(globalX, localVar); cout << "\n참조 처리 후:" << endl; cout << "전역 globalX: " << globalX << endl; cout << "지역 localVar: " << localVar << endl; return 0; }

💭 생각해보기: 스코프와 생명주기 퀴즈

Q1. 다음 코드의 출력 결과는?

#include <iostream> using namespace std; int x = 1; void func() { static int x = 2; x++; cout << x << " "; } int main() { func(); func(); cout << x; return 0; }

💡 정답 확인

정답: 3 4 1

  • 첫 번째 func() 호출: 정적변수 x가 2에서 3으로 증가 → 출력 “3 ”
  • 두 번째 func() 호출: 정적변수 x가 3에서 4로 증가 → 출력 “4 ”
  • main에서: 전역변수 x 출력 → “1”

정적변수는 함수 호출 간에 값이 유지되고, 전역변수와는 별개입니다.

Q2. 다음 중 변수의 스코프 규칙으로 올바른 것은?

  1. 전역변수는 어디서든 접근할 수 있다
  2. 지역변수는 선언된 블록 내에서만 유효하다
  3. 정적변수는 프로그램 종료까지 살아있다
  4. 모든 설명이 맞다

💡 정답 확인

정답: 4번 - 모든 설명이 맞다

  • 1번: ✅ 전역변수는 프로그램 전체에서 접근 가능
  • 2번: ✅ 지역변수는 선언된 스코프 내에서만 유효
  • 3번: ✅ 정적변수는 프로그램 시작부터 끝까지 생존
  • 4번: ✅ 모든 설명이 정확함

🏆 연습 문제

다음 예제들을 직접 만들어보세요:

  1. 🔢 번호 생성기:

    • 정적변수를 사용해 호출할 때마다 증가하는 번호 반환
    • 함수: getNextNumber()
  2. 📊 방문자 카운터:

    • 전역변수: 사이트 이름
    • 정적변수: 총 방문자 수
    • 함수: visitPage() - 방문자 수 증가 및 출력
  3. 🎯 함수 호출 추적:

    • 정적변수: 각 함수의 호출 횟수
    • 여러 함수에서 각각 호출 횟수 관리

💡 힌트:

// 예시: 번호 생성기 int getNextNumber() { static int counter = 1000; // 정적변수 - 처음 한 번만 초기화 return counter++; // 현재 값 반환 후 증가 } // 예시: 방문자 카운터 void visitPage() { static int visitors = 0; // 정적변수 - 방문자 수 visitors++; cout << "방문자 수: " << visitors << "명" << endl; }

🎯 전체 예제 코드

#include <iostream> #include <string> using namespace std; // 전역변수 - 사이트 정보 string siteName = "My Website"; // 번호 생성기 int getNextNumber() { static int counter = 1000; return counter++; } // 방문자 카운터 void visitPage() { static int visitors = 0; visitors++; cout << siteName << " 방문자: " << visitors << "명" << endl; } // 함수별 호출 추적 void functionA() { static int callCount = 0; callCount++; cout << "함수 A 호출 횟수: " << callCount << endl; } void functionB() { static int callCount = 0; callCount++; cout << "함수 B 호출 횟수: " << callCount << endl; } int main() { cout << "=== 정적변수 활용 예제 ===\n" << endl; // 번호 생성기 테스트 cout << "생성된 번호: " << getNextNumber() << endl; cout << "생성된 번호: " << getNextNumber() << endl; cout << "생성된 번호: " << getNextNumber() << endl; cout << endl; // 방문자 카운터 테스트 visitPage(); visitPage(); visitPage(); cout << endl; // 함수 호출 추적 테스트 functionA(); functionB(); functionA(); functionB(); functionB(); return 0; }

출력:

=== 정적변수 활용 예제 === 생성된 번호: 1000 생성된 번호: 1001 생성된 번호: 1002 My Website 방문자: 1명 My Website 방문자: 2명 My Website 방문자: 3명 함수 A 호출 횟수: 1 함수 B 호출 횟수: 1 함수 A 호출 횟수: 2 함수 B 호출 횟수: 2 함수 B 호출 횟수: 3
Last updated on