Skip to content

10. C++ 범위(C++ Scope)


1. 키워드

  • 유효 범위(Scope)
  • 연결(Linkage)
  • 자동 변수(Automatic Variable)와 지역 변수(Local Variable)
  • 레지스터 변수(Register Variable)와 register 키워드
  • 정적 변수(Static Variable)
  • 연결을 가지지 않는 정적 변수와 static 키워드
  • 내부 연결을 가지는 정적 변수와 static 키워드
  • 외부 연결을 가지는 정적 변수와 extern 키워드
  • 네임스페이스(Namespace)와 namespace 키워드
  • ::(범위 지정 연산자), using 지시자(Directive), using 선언(Declaration)


2. 유효 범위와 연결

  • 유효 범위란 하나의 변환 단위(Translation Unit) 내에서 해당 변수가 사용될 수 있는 범위를 나타낸다.
  • 함수 내에서 선언된 변수는 함수 내에서만 사용할 수 있다.
  • 또한, 함수 밖에서 선언된 변수는 변수가 선언된 이후에 나오는 모든 함수에서 사용할 수 있다.
  • C++ 표준에서는 파일(File)이라는 용어 대신에 변환 단위라는 용어를 사용하고 있다.


  • 연결이란 해당 변수를 사용할 수 있는 파일의 접근 가능 여부를 나타낸다.
  • 외부 연결(External Linkage)을 가지는 변수는 여러 파일에서 사용할 수 있다.
  • 하지만 내부 연결(Internal Linkage)을 가지는 변수는 하나의 파일에서만 사용할 수 있다.
  • 또한, 함수 내에서 선언된 변수는 함수 밖에서는 사용할 수 없으므로 연결을 가지지 않는다.


  • C++에서 변수는 유효 범위, 연결 등을 기준으로 다음과 같이 나눌 수 있다.


1] 자동 변수

2] 레지스터 변수

3] 정적 변수


1) 자동 변수

  • 자동 변수란 블록 내에서 선언된 변수를 의미하며, 함수의 매개변수도 여기에 포함된다.
  • 이러한 자동 변수는 변수가 선언될 블록 내에서만 유효하며, 블록이 종료되면 메모리에서 사라진다.
  • 자동 변수는 메모리 상의 스택(Stack) 영역에 저장되며, 초기화하지 않으면 쓰레깃값으로 초기화된다.
  • 이러한 자동 변수는 C의 지역 변수와 같은 의미로 사용된다.


#include <iostream>
using namespace std;

void Local(void);

int main(void) {
    int i = 5;
    int var = 10;
    cout << "main() 함수 내의 자동 변수 var의 값은 "
         << var << "입니다." << endl;

    if (i < 10) {
        Local();
        int var = 30;

        cout << "if 문 내의 자동 변수 var의 값은 "
             << var << "입니다." << endl;
    }

    cout << "현재 자동 변수 var의 값은 "
         << var << "입니다." << endl;

    return 0;
}

void Local(void) {
    int var = 20;

    cout << "Local() 함수 내의 자동 변수 var의 값은 "
         << var << "입니다." << endl;
}

// main() 함수 내의 자동 변수 var의 값은 10입니다.
// Local() 함수 내의 자동 변수 var의 값은 20입니다.
// if 문 내의 자동 변수 var의 값은 30입니다.
// 현재 자동 변수 var의 값은 10입니다.


  • 위의 코드에서 변수 var는 한 번은 main() 함수 내에서, 또 한 번은 if 문에서, 마지막은 Local() 함수 내에서 선언된다.
  • 이처럼 같은 이름의 변수 var는 모두 다른 {}(중괄호) 영역에서 선언되었으며, 이러한 {} 영역을 블록(Block)이라고 한다.
  • 이렇게 변수의 유효 범위는 변수가 선언된 블록을 기준으로 설정되며, 해당 블록이 끝나면 모든 자동 변수는 메모리에서 사라지게 된다.


  • 위의 코드에서처럼 변수의 이름으로 같은 이름을 여러 번 사용하는 것은 매우 잘못된 방법이다.
  • 그리고 한 블록 내에서 같은 이름의 변수를 또다시 선언하려고 하면 컴파일러는 오류를 발생시킨다.


2) 레지스터 변수

  • 레지스터 변수란 변수를 선언할 때 register 키워드를 붙여 선언한 변수를 의미한다.
  • C에서 이렇게 선언된 레지스터 변수는 CPU의 레지스터(Register) 메모리에 저장되어 빠르게 접근할 수 있게 된다.
  • 하지만 컴퓨터의 레지스터는 매우 작은 크기의 메모리이므로, 이 영역에 변수를 선언하기 힘든 경우도 많다.
  • 그럴 때 C 컴파일러는 해당 변수를 그냥 지역 변수로 선언하게 된다.
  • C++11 이전에는 C++에서도 register 키워드가 위와 같은 의미로 사용되었다.
  • 하지만 C++11부터는 단순히 해당 변수가 자동 변수라는 것을 의미하는 역할로만 사용되고 있다.


3) 정적 변수

  • C++에서 정적 변수는 프로그램이 실행되는 내내 유지되는 변수를 의미한다.
  • 이러한 정적 변수는 메모리 상의 데이터(Data) 영역에 저장되며, 초기화하지 않으면 0으로 자동 초기화된다.
  • C++에서는 다음과 같이 세 가지 유형의 정적 변수를 제공하고 있다.


1] 연결을 가지지 않는 정적 변수

2] 내부 연결을 가지는 정적 변수

3] 외부 연결을 가지는 정적 변수


(1) 연결을 가지지 않는 정적 변수

  • 연결을 가지지 않는 정적 변수는 블록 내부에서 static 키워드를 사용하여 정의한다.
  • 이러한 연결을 가지지 않는 정적 변수는 지역 변수와 전역 변수의 특징을 모두 가지게 된다.
  • 이 변수는 전역 변수처럼 단 한 번만 초기화되며, 프로그램이 종료되어야 메모리 상에서 사라진다.
  • 또한, 이렇게 선언된 변수는 지역 변수처럼 해당 블록 내에서만 접근할 수 있다.


#include <iostream>
using namespace std;

void Local(void);
void StaticVar(void);

int main(void) {
    for (int i = 0; i < 3; i++)
        Local();

    for (int i = 0; i < 3; i++)
        StaticVar();

    return 0;
}

void Local(void) {
    int count = 1;

    cout << "Local() 함수가 "
         << count << "번째 호출되었습니다." << endl;
    count++;
}

void StaticVar(void) {
    static int static_count = 1;  // 연결을 가지지 않는 정적 변수

    cout << "StaticVar() 함수가 "
         << static_count << "번째 호출되었습니다." << endl;
    static_count++;
}

// Local() 함수가 1번째 호출되었습니다.
// Local() 함수가 1번째 호출되었습니다.
// Local() 함수가 1번째 호출되었습니다.
// StaticVar() 함수가 1번째 호출되었습니다.
// StaticVar() 함수가 2번째 호출되었습니다.
// StaticVar() 함수가 3번째 호출되었습니다.


  • 위의 코드는 지역 변수로 선언된 count와 연결을 가지지 않는 정적 변수로 선언된 static_count를 서로 비교하는 것이다.
  • 지역 변수인 count는 함수의 호출이 끝날 때마다 메모리 상에서 사라진다.
  • 하지만 연결을 가지지 않는 정적 변수인 static_count는 함수의 호출이 끝나도 메모리 상에서 사라지지 않는다.
  • 그러므로 다음 함수 호출 때에도 이전의 데이터를 그대로 저장하고 있다.
  • 또한, static_count는 전역 변수와는 달리 자신이 선언된 StaticVar() 함수 이외의 영역에서는 호출할 수 없다.


(2) 내부 연결을 가지는 정적 변수

  • 내부 연결을 가지는 정적 변수는 유효 범위를 변환 단위로 가지는 변수에 static 키워드를 사용하여 정의한다.
  • 이러한 내부 연결을 가지는 정적 변수는 해당 변수를 포함하고 있는 변환 단위, 즉 현재 파일에서만 사용할 수 있다.
  • 이 변수는 하나의 파일 내의 모든 블록에서 접근할 수 있고, 또한 사용할 수 있다.


#include <iostream>
using namespace std;

static int var;  // 내부 연결을 가지는 정적 변수
void Local(void);

int main(void) {
    cout << "변수 var의 초깃값은 " << var << "입니다." << endl;

    int i = 5;
    int var = 10;  // 자동 변수 선언

    cout << "main() 함수 내의 자동 변수 var의 값은 "
         << var << "입니다." << endl;

    if (i < 10) {
        Local();

        cout << "현재 변수 var의 값은 "
             << var << "입니다." << endl;  // 자동 변수에 접근
    }

    cout << "더 이상 main() 함수에서는 정적 변수 var에 접근할 수 없습니다." << endl;

    return 0;
}

void Local(void) {
    var = 20;  // 정적 변수의 값 변경

    cout << "Local() 함수 내에서 접근한 정적 변수 var의 값은 "
         << var << "입니다." << endl;
}

// 변수 var의 초깃값은 0입니다.
// main() 함수 내의 자동 변수 var의 값은 10입니다.
// Local() 함수 내에서 접근한 정적 변수 var의 값은 20입니다.
// 현재 변수 var의 값은 10입니다.
// 더 이상 main() 함수에서는 정적 변수 var에 접근할 수 없습니다.


  • 위의 코드에서 정적 변수 var와 같은 이름의 자동 변수 varmain() 함수 내부에서 선언된다.
  • 이 자동 변수가 선언되기 전까지 main() 함수에서도 정적 변수 var에 접근할 수 있다.
  • 하지만 자동 변수 var가 선언된 후에는 main() 함수에서 정적 변수 var로 접근할 방법이 없어진다.
  • 왜냐하면, 블록 내에서 선언되 자동 변수는 같은 이름의 정적 변수를 덮어쓰기 때문이다.
  • C++에서는 하나의 파일 내의 모든 블록에서 데이터를 공유하기 위한 또 다른 방법으로 네임스페이스를 제공하고 있다.


(3) 외부 연결을 가지는 정적 변수

  • 외부 연결을 가지는 정적 변수는 유효 범위를 변환 단위로 가지는 변수를 의미한다.
  • 이러한 외부 연결을 가지는 정적 변수를 전역 변수(Global Variable) 또는 외부 변수(Extern Variable)라고 한다.
  • 외부 변수는 해당 파일뿐만 아니라 외부 파일에서도 사용할 수 있는 변수이다.
  • 이러한 외부 변수는 해당 변수를 사용하는 모든 파일에서 각각 extern 키워드를 사용하여 재선언되어야 사용할 수 있다.


// 파일1.cpp
#include <iostream>
using namespace std;

int var = 10;  // 외부 변수 정의
...
// 파일2.cpp
#include <iostream>
using namespace std;

#include "파일1.cpp"
extern int var;  // 외부 변수를 사용하기 위한 재선언
...


  • C++에서는 여러 파일에서 외부 변수가 사용될 경우, 오직 한 개의 파일에서만 외부 변수에 대한 정의를 할 수 있다.
  • 이러한 원칙을 단일 정의의 원칙이라고 한다.


4) 변수의 종류

변수 종류 유효 범위 연결 선언 위치 키워드
자동 변수 블록 없음 블록 내부 없음
레지스터 변수 블록 없음 블록 내부 register
연결을 가지지 않는 정적 변수 블록 없음 블록 내부 static
내부 연결을 가지는 정적 변수 변환 단위 내부 함수 외부 static
외부 연결을 가지는 정적 변수 변환 단위 외부 함수 외부 없음


3. 네임스페이스

  • C++에서는 변수, 함수, 구조체, 클래스 등을 서로 구분하기 위해서 이름으로 사용되는 다양한 내부 식별자(Identifier)를 가지고 있다.
  • 하지만 프로그램이 복잡해지고 여러 라이브러리가 포함될수록 내부 식별자 간에 충돌할 가능성도 그만큼 커진다.
  • 이러한 이름 충돌 문제를 C++에서는 네임스페이스를 통해 해결하고 있다.
  • C++에서 네임스페이스란 내부 식별자에 사용될 수 있는 유효 범위를 제공하는 선언적 영역을 의미한다.


1) 네임스페이스의 정의

  • C++에서는 namespace 키워드를 사용하여 사용자가 새로운 네임스페이스를 정의할 수 있다.
  • 이러한 네임스페이스는 전역 위치뿐만 아니라 다른 네임스페이스 내에서도 정의될 수 있다.
  • 하지만 블록 내에서는 정의될 수 없으며, 기본적으로 외부 연결을 가지게 된다.
  • 일반적으로 네임스페이스는 헤더 파일에서 정의되며, 언제나 새로운 이름을 추가할 수 있도록 개방되어 있다.


// namespace.h
namespace kang
{
    void Display();  // 함수의 원형
    int count;       // 변수의 선언
}

namespace kim
{
    double display;  // 변수의 선언
    int count;       // 변수의 선언
}


  • C++에서는 전역 네임스페이스라고 하는 파일 수준의 선언 영역이 존재한다.
  • 일반적으로 식별자의 네임스페이스가 명시되지 않으면, 전역 네임스페이스에 자동으로 포함되게 된다.
  • 또한, C++ 표준 라이브러리 타입과 함수들은 std 네임스페이스 또는 그 속에 중첩된 네임스페이스에 선언되어 있다.


2) 네임스페이스로의 접근

  • 네임스페이스를 정의한 후에는 해당 네임스페이스로 접근할 수 있는 방법이 필요하다.
  • 네임스페이스에 접근하기 위해서는 ::(범위 지정 연산자)를 사용하여, 해당 이름을 특정 네임스페이스로 제한하면 된다.


#include "namespace.h"

...
kang::count = 4;      // 네임스페이스 kang에 접근하여 count 변수 사용
kim::display = 3.14;  // 네임스페이스 kim에 접근하여 display 변수 사용
kim::count = 100;     // 네임스페이스 kim에 접근하여 count 변수 사용
...


3) 간소화된 네임스페이스로의 접근

  • 네임스페이스에 속한 이름(변수, 함수, 구조체, 클래스 등)을 사용할 때마다 ::를 사용하여 이름을 제한하는 것은 매우 불편하다.
  • 또한, 길어진 코드로 인해 가독성 또한 떨어지게 된다.
  • C++에서는 이러한 불편함을 해소할 수 있도록 다음과 같은 방법을 제공하고 있다.


1] using 지시자

2] using 선언


(1) using 지시자

  • using 지시자는 명시한 네임스페이스에 속한 이름을 모두 가져와 ::를 사용하지 않고도 사용할 수 있게 해준다.
  • 전역 범위에서 사용된 using 지시자는 해당 네임스페이스의 모든 이름을 전역적으로 사용할 수 있게 만들어준다.
  • 또한, 블록 내에서 사용된 using 지시자는 해당 블록에서만 해당 네임스페이스의 모든 이름을 사용할 수 있게 해준다.


  • C++에서 using 지시자를 사용하는 문법은 다음과 같다.


using namespace 네임스페이스이름;


(2) using 선언

  • using 지시자가 명시한 네임스페이스의 모든 이름을 사용할 수 있게 했다면, using 선언은 단 하나의 이름만을 ::를 사용하지 않고도 사용할 수 있게 해준다.
  • 또한, using 지시자와 마찬가지로 using 선언이 나타나는 선언 영역에서만 해당 이름을 사용할 수 있게 해준다.


  • C++에서 using 선언을 사용하는 문법은 다음과 같다.


using 네임스페이스이름::이름;

References