소년코딩

04.03 - 전역 변수와 링크 (Global variable and linkage)

함수 내부에서 정의된 변수를 지역 변수(local variable)라고 한다. 지역 변수는 블록 스코프(정의된 블록 내에서만 접근 가능)가 있고 자동 주기(정의 지점에서 생성, 블록이 종료되면 소멸)가 있다.

함수 외부에서 선언된 변수를 전역 변수(global variable)라고 한다. 전역 변수는 정적 주기(static duration)로, 프로그램이 시작할 때 생성되고 프로그램이 종료할 때 파괴된다. 전역 변수는 파일 스코프(or 전역 스코프)를 가진다. 이것은 전역 변수가 정의된 시점부터 정의된 소스 파일의 끝까지 접근 가능하다는 것을 의미한다.


전역 변수 정의하기 (Defining global variables)

일반적으로 전역 변수는 소스 코드의 맨 위에 있는 #include 아래에 정의하지만, 어디든 상관없다.

전역 변수의 예:
#include <iostream>

// 함수 외부에서 정의된 변수는 전역 변수(global variable)다.
int g_x;          // global variable g_x
const int g_y(2); // global variable g_y

void doSomething()
{
    // 전역 변수가 정의된 이후, 프로그램 어디에서든지 접근 가능하다.
    std::cout << g_y << "\n";
}

int main()
{
    doSomething();

    // 전역 변수가 정의된 이후, 프로그램 어디에서든지 접근 가능하다.
    g_x = 5;
    std::cout << g_y << "\n";

    return 0;
}

중첩된 블록(nested block)이 이름이 같은 외부 블록의 변수를 숨기는 것처럼, 전역 변수와 같은 이름을 가진 지역 변수는 전역 변수를 숨긴다. 그러나 전역 범위 연산자(::)를 사용하면 컴파일러는 지역 변수 대신 전역 변수를 사용한다.

#include <iostream>
int value(5); // 전역 변수

int main()
{
    int value = 7; // 전역 변수를 숨긴다. (shadowing or hide)
    value++;   // 지역 변수를 증가시킨다.
    ::value--; // 전역 변수를 감소시킨다.

    std::cout << "global value: " << ::value << "\n";
    std::cout << "local value: " << value << "\n";
    return 0;
} // 지역 변수 value는 소멸된다.

This code prints:

global value: 4
local value: 8

그러나 전역 변수와 같은 지역 변수를 정의하는 건 피해야 한다. 관습에 따라 일반적으로 전역 변수를 정의할 때는 g_ 접두사를 붙인다. 이 방법은 전역 변수를 식별하는데 편리할 뿐만 아니라 지역 변수와 충돌을 방지하는 데 도움이 된다.


static과 extern 키워드를 이용한 내부/외부 링크 (Internal and external linkage via the static and extern keywords)

변수는 스코프(scope)와 주기(duration) 외에도 링크(linkage)라는 세 번째 속성이 있다. 링크는 같은 이름의 여러 식별자가 같은 식별자를 참조하는지를 결정한다.

링크가 없는 변수는 정의된 제한된 범위에서만 참조할 수 있다. 지역 변수가 링크가 없는 변수의 예이다. 이름은 같지만 다른 함수에서 정의된 지역 변수는 링크가 없다. 각 변수는 독립적이다.

내부 링크가 있는 변수를 static 변수라고 한다. static 변수는 변수가 정의된 소스 파일 내에서 어디서나 접근할 수 있지만, 소스 파일 외부에서는 참조할 수 없다.

외부 링크가 있는 변수를 extern 변수라고 한다. extern 변수는 정의된 소스 파일과 다른 소스 파일 모두에서 접근할 수 있다.

하나의 파일 내에서만 접근할 수 있는 전역 변수를 생성하려면 다음과 같이 static 키워드를 사용한다:
static int g_x; // g_x is static, and can only be used within this file

int main()
{
    return 0;
}
마찬가지로 전역 변수를 외부에서도 접근할 수 있게 만들려면 extern 키워드를 사용하면 된다:
extern double g_y(9.8); // g_y is external, and can be used by other files

// Note: those other files will need to use a `forward declaration` to access this external variable
// We'll discuss this in the next section

int main()
{
    return 0;
}

기본적으로 전역 변수는 extern 변수로 간주한다. 그러나 상수(const) 전역 변수는 static 변수로 간주된다.


extern 키워드를 통한 변수 전방 선언 (Variable forward declarations via the extern keyword)

다른 소스 파일에서 선언된 외부 전역 변수를 사용하려면 '변수 전방 선언(variable forward declarations)'을 해야 한다.

extern 키워드는 두 가지 다른 의미가 있다. 어떤 상황에서는 extern 키워드가 '외부 링크가 있는 변수를 의미' 하고 다른 상황에서는 '다른 어딘가에서 정의된 변수에 대한 전방 선언'을 의미한다.

다음은 변수 전방 선언의 예제다:

global.cpp:
// 두 개의 전역 변수를 정의한다.
// non-const globals have external linkage by default
int g_x;           // external linkage by default
extern int g_y(2); // external linkage by default, so this extern is redundant and ignored

// in this file, g_x and g_y can be used anywhere beyond this point
main.cpp:
#include <iostream>
#include "global.cpp"

extern int g_x; // forward declaration for g_x (defined in global.cpp) -- g_x can now be used beyond this point in this file
int main()
{
    extern int g_y; // forward declaration for g_y (defined in global.cpp) -- g_y can be used beyond this point in main() only
    g_x = 5;
    std::cout << g_y; // should print 2

    return 0;
}

만약 변수 전방 선언이 함수 외부에서 선언되면 소스 파일 전체에 적용된다. 함수 내에서 선언되면 해당 블록 내에서만 적용된다.

변수가 static으로 선언된 경우, 이에 접근하기 위해 변수 전방 선언을 해도 적용되지 않는다.

constant.cpp:
static const double g_gravity(9.8);
main.cpp:
#include <iostream>
#include "constant.cpp"

extern const double g_gravity; // This will satisfy the compiler that g_gravity exists

int main()
{
    std:: cout << g_gravity; // This will cause a linker error because the only definition of g_gravity is inaccessible from here
    return 0;
}

함수 링크 (function linkage)

함수는 변수와 같은 링크 속성을 가진다. 함수는 항상 외부 링크로 기본 설정되지만 static 키워드를 통해 내부 링크로 설정할 수 있다.

// This function is declared as static, and can now be used only within this file
// Attempts to access it via a function prototype will fail
static int add(int x, int y)
{
    return x + y;
}

함수 전방 선언에는 extern 키워드가 필요하지 않다. 컴파일러는 함수 몸체인지 함수 원형인지 알아서 판단한다.


요약 (Summary)

전역 변수는 전역 스코프(=범위)를 가지며 프로그램의 모든 위치에서 사용할 수 있다. 다른 파일에서 정의된 변수에 접근하려면 키워드 extern을 통해 전방 선언을 해야 한다.

기본적으로 비-상수(not const) 전역 변수는 외부 링크 속성을 가지고 있다. 원하는 경우 static 키워드를 통해 명시적으로 내부 링크 속성을 가지게 할 수 있다. 반대로 상수(const) 전역 변수는 내부 외부 링크 속성을 기본으로 가진다. 원하는 경우 extern 키워드를 통해 외부 링크 속성으로 만들 수 있다.

관습적으로 g_ 접두사를 통해 전역 변수를 식별하기 편하게 한다.

전역 변수:

// Uninitialized definition:
int g_x;        // defines uninitialized global variable (external linkage)
static int g_x; // defines uninitialized static variable (internal linkage)
const int g_x;  // not allowed: const variables must be initialized

// Forward declaration via extern keyword:
extern int g_z;       // forward declaration for global variable defined elsewhere
extern const int g_z; // forward declaration for const global variable defined elsewhere

// Initialized definition:
int g_y(1);        // defines initialized global variable (external linkage)
static int g_y(1); // defines initialized static variable (internal linkage)
const int g_y(1);  // defines initialized static variable (internal linkage)

// Initialized definition w/extern keyword:
extern int g_w(1);       // defines initialized global variable (external linkage, extern keyword is redundant in this case)
extern const int g_w(1); // defines initialized const global variable (external linkage)

cpp 번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/42-global-variables/ 입니다.

댓글 로드 중…

블로그 정보

소년코딩 - 소년코딩

소년코딩, 자바스크립트, C++, 물리, 게임 코딩 이야기

최근에 게시된 이야기