전역 변수
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
전역 변수는 프로그램 내 모든 함수에서 접근 가능한 변수를 의미하며, 동시 스레드 간 정보 전달 등에 사용된다. C++을 포함한 일부 언어에서는 전역 네임스페이스 문제를 야기할 수 있으며, 적절한 잠금 장치 없이는 스레드 안전성을 보장하지 못한다. 전역 변수는 코드 이해를 어렵게 하고, 유지 보수 및 테스트를 복잡하게 만들 수 있다. C/C++에서는 파일 범위 변수와 `extern` 키워드를 사용하여 전역 변수를 선언하며, Java는 클래스 변수, PHP는 `$GLOBALS` 슈퍼글로벌과 같은 방식으로 전역 변수와 유사한 기능을 제공한다. 전역 변수는 초기화 순서에 의존하는 코드를 작성 시 예상치 못한 동작을 유발할 수 있으며, 객체 지향 언어에서는 클래스 정적 필드가 전역 변수와 유사한 역할을 한다.
더 읽어볼만한 페이지
- 변수 (컴퓨터 과학) - 멤버 변수
멤버 변수는 객체 지향 프로그래밍에서 객체의 속성을 저장하고 관리하며 객체의 상태를 나타내는 변수로, 지역 변수와 달리 객체의 생명 주기와 함께 값을 유지한다. - 변수 (컴퓨터 과학) - 정적 변수
정적 변수는 프로그램 실행 시간 동안 값을 유지하며, C 언어에서 `static` 키워드로 정의되어 함수 호출 간에 값을 유지하고, 객체 지향 프로그래밍에서 클래스의 모든 인스턴스에서 공유되는 클래스 변수로 사용된다. - 프로그래밍 구성체 - 형 변환
형 변환은 프로그래밍에서 변수의 데이터 타입을 변경하는 것으로, 암시적 형 변환과 명시적 형 변환으로 나뉘며, 객체 지향 프로그래밍에서는 업캐스팅과 다운캐스팅이 발생하고, 각 언어는 고유한 규칙과 방법을 제공하며 잘못된 형 변환은 오류를 유발할 수 있다. - 프로그래밍 구성체 - 연산자 오버로딩
연산자 오버로딩은 프로그래밍 언어에서 기존 연산자를 사용자 정의 자료형에 대해 재정의하여 내장 자료형처럼 다루도록 하는 기능으로, 코드 가독성과 표현력을 높이지만 남용 시 코드 의미를 모호하게 만들 수 있다.
전역 변수 | |
---|---|
일반 정보 | |
유형 | 변수 |
범위 | 전역 범위 |
특징 | |
접근성 | 프로그램 전체에서 접근 가능 |
생존 기간 | 프로그램 실행 시작부터 종료까지 |
이름 충돌 가능성 | 높음 |
장점 | |
편의성 | 여러 함수에서 공유하는 데이터에 쉽게 접근 가능 |
전역 설정 | 프로그램 전체에 적용되는 설정을 쉽게 관리 가능 |
단점 | |
이름 충돌 | 다른 코드와의 이름 충돌 가능성 증가 |
디버깅 어려움 | 값이 어디에서 변경되었는지 추적하기 어려움 |
모듈성 감소 | 코드의 모듈성과 재사용성 감소 |
부작용 | 예기치 않은 부작용 발생 가능성 증가 |
사용 시 주의사항 | |
최소화 | 필요한 경우에만 사용하고 남용을 피함 |
명확한 이름 | 의미를 명확하게 나타내는 이름을 사용 |
캡슐화 | 가능한 경우 캡슐화하여 접근을 제한 |
의존성 관리 | 전역 변수에 대한 의존성을 명확하게 관리 |
2. 사용
전역 변수는 스레드나 시그널 핸들러와 같이 호출자와 피호출자 관계를 공유하지 않는 코드 섹션 간에 정보를 전달하는 데 널리 사용된다.[1] C를 포함하여 각 파일이 암시적 네임스페이스를 정의하는 언어는 전역 네임스페이스가 있는 언어에서 발생하는 문제 대부분을 제거하지만, 적절한 캡슐화가 없으면 일부 문제가 지속될 수 있다.[1] 뮤텍스와 같은 적절한 잠금이 없으면 전역 변수를 사용하는 코드는 보호된 메모리의 읽기 전용 값을 제외하고 스레드 안전하지 않다.[1]
환경 변수는 일부 운영 체제에서 제공하는 기능이다. OS의 셸(ksh in 유닉스, bash in 리눅스, COMMAND.COM in 도스 및 CMD.EXE in 윈도우) 내에서 일종의 변수이다. 예를 들어 유닉스 및 관련 시스템에서 일반 변수는 `export` 키워드를 사용하면 환경 변수가 된다. 셸 이외의 프로그램 코드는 `getenv()` 및 `setenv()`와 같은 API 호출을 통해 이에 접근해야 한다.
구조적 프로그래밍이 아닌 여러 언어들, 예를 들어 초기 버전의 BASIC, COBOL, 포트란 I (1956)은 전역 변수만 제공했다. 포트란 II (1958)는 지역 변수를 갖는 서브루틴을 도입했고, 전역 변수에 접근하기 위한 `COMMON` 키워드를 도입했다. 포트란 77에서도 `COMMON`의 사용은 계속되었으며,[1] 이후의 PL/SQL과 같은 언어에도 영향을 미쳤다. 전역 변수를 위한 명명된 `COMMON` 그룹은 구조화된 네임스페이스처럼 작동한다.[2] 포스, Lua, Perl, 그리고 대부분의 셸에서도 변수는 기본적으로 전역 변수이다.
일반적으로 전역 변수는 그 비국소적인 성질 때문에 나쁜 관행으로 여겨진다.[1] 전역 변수는 어디에서든 변경될 가능성이 있고 프로그램의 일부가 이에 의존하게 될 수 있기 때문이다.[1] 따라서 전역 변수는 상호 의존성을 만들어낼 무한한 가능성을 가지고 있으며, 상호 의존성이 높아지는 것은 복잡성을 증가시킨다.[1] action at a distance|원격 작용 (정보 공학)영어을 참조.
그러나 전역 변수가 적합한 상황도 있다.[1] 예를 들어 시스템 시작 시 한 번만 파일에서 읽어들여 이후 다양한 함수를 통해 지속적이고 자주 사용되는 설정 정보를 저장하는 변수는, 함수에 매번 인수로 전달하는 것을 피하기 위해 전역 변수로 사용할 수 있다.[1] 또한 병렬로 실행하는 스레드나 시그널 핸들러처럼, 호출 측/피호출 측의 관계를 갖지 않는 코드끼리 섹션 간 정보를 주고받기 위해서도 널리 사용된다.[1] 사용 가능한 메모리 용량이 적은 임베디드 시스템과 같은 엄격한 환경에서는 전역 변수를 사용할 수밖에 없는 경우도 있다.[1]
C 언어처럼 네임스페이스를 갖지 않는 프로그래밍 언어에서는 소스 파일을 넘어 전역 변수를 공유할 때 이름 충돌의 문제도 발생할 수 있다.[1]
각각의 소스 파일이 암묵적인 네임스페이스(익명 네임스페이스)를 정의하는 언어에서는 전역 네임스페이스를 가진 언어에서 보이는 문제의 많은 부분이 해소되지만, 일부 문제는 적절한 캡슐화를 수행하지 않으면 해결할 수 없다.[1] (뮤텍스와 같은) 적절한 잠금 없이 전역 변수를 사용하고 있는 코드는 보호 메모리 내의 읽기 전용 값이 아니면 스레드 세이프하지 않다.[1]
헝가리안 표기법에서는 소스 코드상에서 전역 변수를 사용하고 있음을 명확히 하기 위해 "g_" 접두사를 붙인다.[1]
3. 환경 변수
환경 변수는 설정된 프로세스에 로컬이다. 즉, 두 개의 터미널 창(셸을 실행하는 두 개의 다른 프로세스)을 열고 한 창에서 환경 변수의 값을 변경하면 해당 변경 사항은 다른 창에서는 보이지 않는다.
자식 프로세스가 생성될 때 모든 환경 변수와 해당 값을 부모 프로세스에서 상속받는다. 일반적으로 프로그램이 다른 프로그램을 호출할 때 먼저 fork를 통해 자식 프로세스를 생성한 다음 자식이 필요에 따라 환경을 조정하고 마지막으로 자식이 호출할 프로그램으로 자신을 대체한다. 따라서 자식 프로세스는 환경 변수를 사용하여 피어와 통신할 수 없으므로 원격 작용 문제를 방지한다.
4. 언어별 전역 변수
C/C++, Java, PHP, 기타 언어에서의 전역 변수 사용에 대한 내용은 하위 섹션을 참고하라.
일반적으로 전역 변수는 비국소적인 성질 때문에 나쁜 관행으로 여겨진다. 전역 변수는 잠재적으로 어디에서든 변경될 가능성이 있으며, 프로그램의 일부가 이에 의존하게 될 수 있기 때문이다. 따라서 전역 변수는 상호 의존성을 만들어낼 무한한 가능성을 가지고 있으며, 상호 의존성이 높아지는 것은 복잡성을 증가시킨다. action at a distance|원격 작용 (정보 공학)영어을 참조.
그러나 전역 변수가 적합한 상황도 있다. 예를 들어, 시스템 시작 시 한 번만 파일에서 읽어들여 이후 다양한 함수를 통해 지속적으로 자주 사용되는 설정 정보를 저장하는 변수를, 함수에 매번 인수로 전달하는 것을 피하기 위해 사용할 수 있다. 또한 병렬로 실행하는 스레드나 시그널 핸들러처럼, 호출 측/피호출 측의 관계를 갖지 않는 코드끼리 섹션 간 정보를 주고받기 위해서도 널리 사용된다. 사용 가능한 메모리 용량이 적은 임베디드 시스템과 같은 엄격한 환경에서는, 전역 변수를 사용할 수밖에 없는 경우도 있다.
또한, C 언어처럼 네임스페이스를 갖지 않는 프로그래밍 언어에서는, 소스 파일을 넘어 전역 변수를 공유할 때, 이름 충돌의 문제도 발생할 수 있다.
각각의 소스 파일이 암묵적인 네임스페이스(익명 네임스페이스)를 정의하는 언어에서는, 전역 네임스페이스를 가진 언어에서 보이는 문제의 많은 부분이 해소되지만, 일부 문제는 적절한 캡슐화를 수행하지 않으면 해결할 수 없다. (뮤텍스와 같은) 적절한 잠금 없이 전역 변수를 사용하고 있는 코드는, 보호 메모리 내의 읽기 전용 값이 아니면 스레드 세이프하지 않다.
헝가리안 표기법에서는, 전역 변수를 사용하고 있음을 소스 코드상에서 명확히 하기 위해, "g_"의 접두사를 붙인다.
전역 변수를 사용하면 소프트웨어 소스 코드를 이해하기 어려워진다. 전역 변수의 값은 프로그램 어디에서든지 참조 및 변경할 수 있으므로, 전역 변수가 어떻게 사용되는지, 그리고 전역 변수의 값(즉, 상태)이 어떻게 변화하는지를 이해하기 위해서는 프로그램 전체를 파악해야 하기 때문이다.
DLL과 같은 많은 시스템은 다른 모듈에 있는 전역 변수의 참조를 직접 지원하지 않으므로, 전역 변수가 많이 사용되는 코드를 분할하여 재사용 라이브러리로 만드는 것은 매우 어렵다. 또한, 전역 변수가 다른 지역 또는 객체 스코프 변수로 대체하기 위해 위험한 이름을 만들어서 발생하는 이름 문제를 유발할 수 있다. 전역 변수와 같은 이름의 지역 변수는 일반적으로 해당 지역 변수의 스코프에서 전역 변수를 숨긴다. 따라서, 더욱 코드의 이해를 어렵게 만든다. 전역 변수에 값의 쓰기는 이해와 예측을 어렵게 하는 부작용을 생성한다. 전역 변수의 이용은 유닛 테스트를 목적으로 하는 코드 분할을 더욱 어렵게 한다. 그러므로, 그것들은 직접적으로 코드의 질 저하를 조장한다.
자바(Java)나 C#(C#)와 같은 후발 객체 지향 언어는 전역 변수를 가지지 않지만, 클래스의 정적 필드 (클래스 변수)가 전역 변수에 해당한다. 정적 필드는 한 번만 호출되는 정적 이니셜라이저 또는 정적 생성자에 의해 초기화 처리를 제어할 수 있지만, 전역 변수와 마찬가지로 비국소적인 성질을 내포하고 있으므로, 남용하면 코드 품질의 저하를 초래한다. 특히, 재할당 가능한 정적 필드를 외부에 공개하는 것은 피해야 한다[6]。
4. 1. C/C++
C와 C++에서 전역 변수는 프로그램의 모든 부분에서 접근할 수 있는 변수이다.
C 언어는 `global` 키워드를 지원하지 않지만, 함수 외부에서 선언된 변수는 "파일 범위"를 가진다. 이는 해당 파일 내에서 변수를 볼 수 있다는 의미이다. 이러한 변수는 암묵적으로 외부 연결을 가지며, 다른 파일에서도 `extern` 키워드를 사용하여 접근할 수 있다. `extern` 선언은 일반적으로 공유 헤더 파일에 배치된다.[3]
```cpp
#include
// 파일 범위 변수 (내부 연결)
static int shared = 3;
// 외부 연결을 가지는 변수
extern int over_shared;
// 내부 연결을 가지는 변수
int over_shared_too = 2;
static void ChangeShared() {
// 파일 범위 변수 참조
shared = 5;
}
static void LocalShadow() {
// 같은 이름의 전역 변수를 가리는 지역 변수
int shared;
shared = 1000; // 지역 변수에만 영향
}
static void ParamShadow(int shared) {
// 매개변수에만 영향
shared = -shared;
}
int main() {
printf("%d\n", shared); // 3 출력
ChangeShared();
printf("%d\n", shared); // 5 출력
LocalShadow();
printf("%d\n", shared); // 5 출력 (전역 변수는 변경되지 않음)
ParamShadow(1);
printf("%d\n", shared); // 5 출력 (전역 변수는 변경되지 않음)
return 0;
}
```[7]
위 예제에서 `shared`는 파일 범위 변수이며, `over_shared`는 외부 연결을 가지는 전역 변수이다. `LocalShadow` 함수 내의 `shared` 변수는 전역 변수 `shared`를 가린다.
C++에서도 함수 외부에서 선언된 변수는 전역 변수가 된다.
```cpp
#include
int global = 3; // 전역 변수
void ChangeGlobal() {
global = 5; // 전역 변수 변경
}
int main() {
std::cout << global << std::endl; // 3 출력
ChangeGlobal();
std::cout << global << std::endl; // 5 출력
return 0;
}
```[7]
전역 변수는 프로그램의 어느 곳에서든 참조 및 변경될 수 있으므로, 코드 이해를 어렵게 만들 수 있다. 전역 변수의 사용은 유닛 테스트를 어렵게 하고, 코드 품질 저하를 유발할 수 있다. 또한, 전역 변수의 초기화 순서는 C/C++ 표준에서 규정되어 있지 않으므로, 초기화 순서에 의존하는 코드는 예기치 않은 동작을 일으킬 수 있다.
4. 2. Java
Java와 같은 일부 언어는 전역 변수를 지원하지 않는다. Java에서 지역 변수가 아닌 모든 변수는 클래스의 필드이다. 따라서 모든 변수는 클래스 또는 메서드의 범위 내에 있다. Java에서 정적 필드(클래스 변수라고도 함)는 클래스의 모든 인스턴스와 독립적으로 존재하며 모든 인스턴스 간에 하나의 복사본이 공유된다. 따라서 공용 정적 필드는 다른 언어의 전역 변수와 유사한 "공유" 동작 때문에 동일한 용도로 많이 사용된다.
4. 3. PHP
PHP는 `global` 키워드와 전역 변수를 사용하는 몇 가지 특이한 방법을 가지고 있다.
함수 외부에서 선언된 변수는 파일 범위(대부분의 경우 가장 넓은 범위)를 갖는다. 그러나 `global` 키워드로 가져오지 않으면 함수 내부에서 접근할 수 없다(즉, 이 키워드는 전역 변수를 ''선언''하는 것이 아니라, 전역 변수에 ''접근''한다).[1]
그러나 ''슈퍼글로벌''로 알려진 몇 가지 미리 정의된 변수는 항상 접근 가능하다.[1] 이들은 모두 배열이다.[1] 범용적인 슈퍼글로벌은 `$GLOBALS`로, 함수 범위 밖에서 정의된 모든 변수를 포함한다.[1] 이 변수의 요소에 대한 변경은 원래 변수를 변경하며, 추가는 새로운 변수를 생성한다.[1] 슈퍼글로벌 `$_POST`와 `$_GET`은 웹 프로그래밍에서 널리 사용된다.[1]
4. 4. 기타 언어
5. 전역 변수의 문제점과 대안
전역 변수는 프로그램의 어느 곳에서든 변경될 수 있어 상호 의존성을 높이고, 이는 복잡성을 증가시킨다.
하지만 시스템 시작 시 한 번만 읽어들이는 설정 정보처럼, 전역 변수가 적합한 경우도 있다. 예를 들어, 설정 정보를 담은 변수를 각 함수에 인수로 전달하는 대신 전역 변수로 사용하면 편리하다. 또한 스레드나 시그널 핸들러처럼 호출 관계가 없는 코드 간 정보를 주고받을 때도 전역 변수가 사용된다. 메모리 용량이 제한된 임베디드 시스템 환경에서는 전역 변수 사용이 불가피할 수 있다.
C 언어와 같이 네임스페이스가 없는 프로그래밍 언어에서는 전역 변수 공유 시 이름 충돌 문제가 발생할 수 있다. 각 소스 파일이 암묵적 네임스페이스(익명 네임스페이스)를 정의하는 언어에서는 전역 네임스페이스 문제의 상당 부분이 해소되지만, 적절한 캡슐화 없이는 일부 문제가 남는다. 적절한 잠금(뮤텍스) 없이 전역 변수를 사용하면 스레드 세이프하지 않다.[6]
헝가리안 표기법에서는 전역 변수임을 명확히 하기 위해 "g_" 접두사를 붙이기도 한다. 전역 변수를 사용하면 프로그램 전체를 파악해야 변수의 사용과 상태 변화를 이해할 수 있어 코드 이해가 어려워진다.
아래는 지역 변수를 사용했을 때의 코드 예시이다.
```cpp
int FunctionA() { return 2; }
int FunctionB(int x) { return x * 10; }
int FunctionC(int x, int y) { return (x + y) / 2; }
int main() {
int x = FunctionA(); // 지역 변수 x를 변경한다
int y = FunctionB(x); // 지역 변수 x를 기반으로 지역 변수 y를 변경한다
int z = FunctionC(x, y); // 지역 변수 x, y를 기반으로 지역 변수 z를 변경한다
return 0;
}
```
지역 변수를 사용하면 함수 호출과 결과를 한눈에 파악하기 쉽다. 각 함수는 독립적이고 입출력이 명확하여 모듈 재사용성이 높다. 반면 전역 변수를 사용하면 함수 선언부와 호출 측만으로는 변수 영향을 파악하기 어렵고, 입출력이 불분명하며 모듈 재사용도 어렵다.
DLL 같은 시스템은 다른 모듈의 전역 변수 참조를 직접 지원하지 않아, 전역 변수가 많은 코드를 분할하여 재사용 라이브러리로 만들기 어렵다. 전역 변수는 이름 충돌 문제를 일으킬 수 있으며, 같은 이름의 지역 변수는 해당 스코프에서 전역 변수를 숨겨 코드 이해를 더욱 어렵게 만든다. 전역 변수 값 변경은 예측 어려운 부작용을 낳고, 유닛 테스트를 위한 코드 분할을 어렵게 하여 코드 품질 저하를 초래한다.
C 언어 및 C++(C++) 표준에서는 전역 변수 초기화 순서가 규정되어 있지 않아, 초기화 순서에 의존하는 코드는 예기치 않은 동작을 유발할 수 있다.
자바(Java)나 C#(C#) 같은 언어는 전역 변수가 없지만, 클래스의 정적 필드(클래스 변수)가 전역 변수와 유사하다. 정적 필드는 정적 이니셜라이저나 정적 생성자로 초기화되지만, 전역 변수처럼 비국소적 성질을 내포하여 남용 시 코드 품질 저하를 초래한다. 특히 재할당 가능한 정적 필드의 외부 공개는 피해야 한다.[6]
참조
[1]
웹사이트
Fortran 77 Tutorial
https://web.stanford[...]
[2]
웹사이트
First Steps: Stack & Heap Objects
http://www-numi.fnal[...]
[3]
서적
C in a Nutshell
O'Reilly
2006
[4]
웹사이트
What are the rules for local and global variables in Python?
https://docs.python.[...]
Python Software Foundation
2020-06-04
[5]
웹사이트
Declare variables as global
http://in.mathworks.[...]
The MathWorks, Inc.
2015-02-07
[6]
웹사이트
OBJ10-J. public static 変数を final 宣言せずに使わない
https://www.jpcert.o[...]
[7]
문서
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com