Notice
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Today
Total
관리 메뉴

Study Note

[C/C++ Syntax] #011 변수의 속성(Attribute of variable) 본문

- 프로그래밍 언어(Programming Language)/C\C++

[C/C++ Syntax] #011 변수의 속성(Attribute of variable)

mymir 2021. 1. 11. 13:01

우리는 여태까지 변수를 사용하면서 의도하였든 그렇지 않았든 그 속성을 구분하면서 사용해왔습니다. 자료형, 변수 이름, 변수 값 등이 속성에 해당됩니다. 이번 글에서는 추가적으로 세 가지의 변수 속성을 알아보도록 하겠습니다.

 

 

 

범위(scope)


#include <iostream>

using namespace std;

int global_variable = 1; // 전역 변수

int main() {
	int local_variable = 2; // 지역 변수
}

가시성(visibility)이라고도 불리는 범위는 변수가 어떤 범위에서 사용이 가능한지 의미하는 속성으로, 별다른 수식어가 필요 없이 변수가 선언되는 위치에 의하여 결정됩니다. 즉, 변수가 항상 프로그램의 시작부터 끝까지 존재하는 것이 아니라 각각 생성 시각과 소멸 시각이 다를 수 있다는 말이지요. 이런 속성은 크게 전역 변수(global variable)와 지역 변수(local variable)로 나눌 수 있습니다.

 

 

우선 전역 변수는 그 어떤 블록(중괄호의 내부)이나 함수에 속해 있지 않은 변수로, 프로그램의 시작에서 생성되어 프로그램의 종료에서 소멸되며, 프로그램 전체에서 사용이 가능한 변수입니다.

전역변수는 위치에 구애받지 않다는 장점이 있지만 그만큼 사용에 유의해야 합니다. 만약 코드가 복잡해지거나 협업을 하게되는 경우 전역 변수의 변경을 놓쳐 프로그램이 오작동할 일이 생길 수 있기 때문입니다. 따라서 전역 변수는 남용하지 말고 분명한 필요에 의해 사용하는 것이 좋습니다.

 

 

그럼 지역 변수는 반대로 특정 블록이나 함수에 속한 변수를 의미합니다. 몇몇 C언어 컴파일러에서는 과거의 방식에 따라 블록의 시작 위치에서만 지역 변수를 선언할 수 있게 되어있지만 C++이나 다른 컴파일러에선 블록의 어디에서나 변수를 선언할 수 있습니다. (다만 변수는 선언 이후에만 사용할 수 있습니다.)

기본적으로 지역 변수는 변수가 선언된 블록 내부에서만 사용하고 접근할 수 있습니다. 때문에 서로 속해있지 않은 블록에 선언된 같은 이름의 지역변수가 존재 가능하며 이름만 같을 뿐 아예 다른 변수가 되는 것입니다.

 

이런 함수의 범위에 의한 두 가지 오해 사항을 짚어보겠습니다.

먼저 함수 호출 시 인수로 전달되는 변수의 이름과 전달 받는 매개변수의 이름이 같은 경우 입니다.

#include <iostream>

using namespace std;

void increase(int x);

int main() {
	int x = 1;
	increase(x);
	cout << x << endl;
}

void increase(int x) {
	x++;
}

단순히 함수가 전달받은 값을 증가시킨다고 생각하여 작성되는 코드입니다. 앞서 말한것 처럼 main함수에서 선언된 x와 increase함수에서 매개변수로 선언된 x는 서로 속해있지 않은 블록에 각각 존재하므로 이름만 같지 서로 다른 변수이며 정확하게는 main함수의 'x'가 전달되는 것이 아니라 'x의 값'이 전달되는 것입니다.

하지만 코딩을 하다보면 서로 다른 지역의 변수를 변경해야 하는 경우가 생일 수 있습니다. 이럴 경우엔 함수의 반환값을 인수로 사용한 변수에 다시 대입하는 방법이 있고, 포인터를 이용한 방법이 있습니다. 이 부분에 대해선 뒤에서 더 자세히 다루겠습니다.

 

 

마지막으로 전역 변수와 지역 변수의 이름이 동일한 경우입니다. 뿐만 아니라 블록이 중첩된 경우에서도 바깥 블록의 변수와 안쪽 블록의 변수의 이름이 같은 경우도 마찬가지입니다.

#include <iostream>

using namespace std;

int value = 10;

int main() {
	int value = 20;

	cout << value << endl;
}

변수를 사용하는 가장 블록 기준 가장 가까운 블록에 선언된 변수가 우선시되어 사용됩니다. 위의 예시로는 10이 저장된 value는 전역변수, 20이 저장된 value는 main함수에 귀속된 지역변수로 main함수에서 value의 값을 출력시 지역 변수 value의 값인 20이 출력되는 구조입니다. 물론 사용 블록에 직접 선언이 안되어도 다음과 같이 나타날 수 있습니다.

 

 

#include <iostream>

using namespace std;

int value = 10;

int main() {
	int value = 20;

	{
		cout << value << endl;
		int value = 30;
		{
			cout << value << endl;
		}
	}
}

 

 

생존 시간(life time)


생존 시간은 변수가 생성되어 소멸하기까지의 시간을 말합니다. 종종 범위 속성과 헷갈리는 분들이 있으니 공간과 시간으로 구분하며 이해하는 것이 좋습니다. 이런 생존시간은 자동 할당(automatic allocation)정적 할당(static allocation)동적 할당(dynamic allocation)으로 구분됩니다.

 

자동 할당은 지역 변수처럼 특정 블록을 기준으로 블록의 시작에서 생성되어 블록이 종료되면 소멸되는 방법입니다. C에서는 변수 생성 시 아래와 같이 저장 유형 지정자 auto를 붙여 사용하지만, 어차피 블록으로 구분되기 때문에 auto는 생략하고 사용합니다. 심지어 C++에서는 타입 추론을 위한 키워드로 아예 다른 의미로 사용됩니다.

#include <iostream>

using namespace std;

int main(){
    auto int x = 1;
}

 

정적 할당은 전역 변수처럼 프로그램 실행 시 생성시켜 프로그램이 종료될 때 소멸시키는 방법입니다. 전역변수가 여기에 포함되고, 아래와 같이 지역 변수에 저장 유형 지정자 static을 붙여 사용하기도 합니다. (전역 변수에 static을 붙이는 것은 아래 연결 속성에서 다루겠습니다.)

#include <iostream>

using namespace std;

int main(){
    static int x = 10;
}

지역 변수에 static을 붙여 블록이 종료되어도 변수가 소멸되지 않는 다는 것은, 반복문이나 함수와 같이 동일한 블록이 여러번 수행되는 경우에 유용하게 사용됩니다.

 

반복문과 함수에서의 실행 예시를 각각 들어보겠습니다.

#include <iostream>

using namespace std;

int main() {

	for (int i = 0; i < 10; i++) {
		int a = 10;
		static int b = 10;
		cout << "a = " << a << ", b = " << b << endl;
		a++;
		b++;
	}
}

 

 

#include <iostream>

using namespace std;

void func() {
	int a = 10;
	static int b = 10;
	cout << "a = " << a << ", b = " << b << endl;
	a++;
	b++;
}

int main() {

	for (int i = 0; i < 10; i++) {
		func();
	}
}

 

 

연결(linkage)


우리는 여태까지 하나의 프로그램을 하나의 소스 파일만으로 구성했지만, 여러 개의 소스 파일로도 구성될 수 있습니다. 이런 경우에, 변수가 각 소스파일의 상호 관계에서 연결 상태를 나타낼 수 있어야 합니다. 연결 속성은 무연결(no linkage), 외부 연결(external linkage), 내부 연결(internal linkage)로 나눌 수 있습니다.

 

 

먼저 무연결은 모든 지역 변수가 해당되며, 외부와 연결을 가지지 않는 상태를 말합니다.

 

다음으로 외부 연결은 전역 변수 중 두 개 이상의 소스 파일에서 사용되도록 하는 방법을 말합니다. 한 파일에서 기본 전역 변수로 선언한 변수를 다른 파일에선 extern을 사용하여 선언하면 이어서 사용이 가능하다는 말입니다.

// file1.cpp
#include <iostream>

int x = 10;

int main(){}
// file2.cpp
#include <iostream>

using namespace std;

extern int x;

int main(){
    cout << x << endl; // 10
}

 

그렇다면 내부 연결은 반대로 선언되는 소스 파일 내부에서만 사용 가능하도록 전역 변수를 선언하는 방법입니다. 저장유형 지정자 static을 붙여 사용합니다.

#include <iostream>

using namespace std;

static int x = 10;

int main(){}

 

이런 지정자 extern, static은 함수에도 붙어 변수와 마찬가지로 각각 함수의 외부연결, 함수의 내부연결로 사용될 수 있습니다.

Comments