끝나지 않는 프로그래밍 일기


1. 지역 변수와 전역 변수


지역 변수(local variable)와 전역 변수(global variable)에 대해서 간단히 설명해보도록 할텐데, 지역 변수란 우리가 선언한 지역을 벗어나면 기억 공간에서 자동으로 소멸합니다. 이 지역변수는 자동 변수(automatic variable)이라고도 부르며 이런 지역변수의 선언을 위한 auto란 키워드가 존재합니다. 참고로 auto는 우리가 따로 명시하지 않아도 지역변수로 선언이 됩니다. 이는 지역변수를 선언하기 위해 따로 auto란 키워드를 사용할 필요가 없다는 말입니다.

C언어에서의 지역(local)은 무엇을 의미하시는지 알고 계시나요? 바로 {와 }를 하나의 지역으로 생각하시면 되겠습니다. 독자분들이 얼마나 잘 이해하고 있는지 테스트하기 위하여, 간단한 문제를 하나 내보도록 하겠습니다. 문제는 바로, 아래 코드에서의 지역(local)은 몇 개인지 한번 생각해보세요.
#include <stdio.h>

int main()
{
	int a;
	{
		int b;
	}
	{
		int c;
	}

	return 0;
}
위 코드에서의 지역은 몇 개일까요? 위에도 말했듯이, 지역은 {와  }를 하나의 지역이라고 말할 수 있으니 총 3개임을 알 수 있습니다. 메인 함수의 영역, 그리고 메인 함수에 포함되어 있는 영역 두개를 포함해서 말이죠. 그럼 문제를 하나 더 내보도록 하겠습니다. 이번에도 역시 간단한 문제입니다. 아래의 코드에서 어떤 결과가 출력될까요?

#include <stdio.h>

int main()
{
	int a = 45;
	{
		int a = 64;
		printf("a: %d\n", a);
	}
	printf("a: %d\n", a);
	return 0;
}
위의 코드에선 아래와 같은 결과를 볼 수 있습니다.

a: 64
a: 45
계속하려면 아무 키나 누르십시오 . . .


코드를 우선 보시면, 바깥 영역에 있는 a는 45로 초기화 되었고 내부 영역에 있는 a는 64로 값이 초기화 되었습니다. 그렇다면 각자 어떤 값을 출력하는지 알아보기 위하여 printf 함수로 그 값을 출력해 보았더니, 위와 같은 결과가 나왔습니다. 왜 위와 같은 결과가 나온 것일까요? 그 이유는, 영역 내에서 선언한 a로 인해 바깥에서 선언된 a 변수가 가려지고, 새로 선언된 a의 값을 출력하는 것입니다. 그리고 영역 바깥을 벗어나는 순간 새롭게 선언된 a는 기억 공간에서 소멸하는 것입니다. 그리고 마지막 출력은 그대로 바깥에 선언된 a의 값을 출력합니다.

이는 변수의 가시성 때문이며, 변수의 가시성이란 A와 B 지역이 있고 B 지역이 A 지역에 포함된다고 가정할 때 A에서 이미 선언된 변수가 있어도 B에서 다시 선언되어 버리면, 이미 선언되어 있는 변수는 무시하고 새롭게 선언된 변수를 우선한다는 특성입니다. (물론 B의 영역에서 a의 선언 없이 a의 값을 출력한다면, A 영역에서의 a 변수값이 출력됩니다.)


그리고 한가지 더 알아두실게 있다면, 위에서 설명했듯이 지역 변수의 생존 영역은 그 변수가 선언된 영역을 벗어나면 메모리 공간에서 그 변수는 소멸한다는 것을 꼭 알아두시고 계세요. (지역 변수와 전역 변수는 추후에 메모리 구조에서 다시 다루게 될 예정입니다.)


자 그럼, 지역 변수는 이쯤 알아두도록 하고 전역 변수(global variable)는 어떤 녀석일까요? 바로 영역 내가 아닌 영역 외부에 선언된 변수를 말합니다. 변수가 선언된 영역에서만 접근이 가능한 지역 변수와는 다르게, 전역 변수는 자신이 선언되어 있는 내부의 어떤 함수에서든 접근이 가능한 녀석을 말합니다.

#include <stdio.h>

int a; 

int main()
{
	printf("a: %d\n", a);
	a = 40;
	printf("a: %d\n", a);

	return 0;
}

결과:

a: 0
a: 40
계속하려면 아무 키나 누르십시오 . . .


위 코드에서 3행에 선언된 변수 보이시나요? 저 변수가 바로 전역 변수이며, 이 전역 변수는 프로그램이 종료될때까지 메모리 공간에 계속 남아있으며, 프로그램이 시작되자 마자 메모리 공간에 할당됩니다. 지역 변수와는 달리 함수 내에서는 따로 선언을 하지 않았으나 모든 함수 내에서 전역 변수인 a를 사용할 수 있습니다. 그리고 전역 변수는 우리가 따로 초기화를 해주지 않아도, 0으로 초기화 되어 있습니다. 이는 결과에서 확인하실 수 있습니다.


전역 변수를 사용하게 되면 편리하나, 그렇다고 많이 쓰는건 좋지 않습니다. 다시 말하지만, 지역 변수와는 달리 블럭 밖에서 선언하게 되면 그게 곧 전역 변수를 선언하는 것이 됩니다.


2. 정적 변수와 외부 변수 


이제는 정적 변수(static variable)와 외부 변수(extern variable)를 알아볼 차례인데, 앞에서 말했듯이 지역 변수는
우리가 선언한 영역을 벗어나면 그 값을 잃고 메모리 공간에서 소멸한다고 했습니다. 하지만 정적 변수를 사용하면 영역을 벗어나도 값을 그대로 유지하며, 메모리 공간에서도 소멸하지 않습니다. 한번 선언하고 나면, 계속 유지가 되는 변수라고 말할 수 있습니다.
#include <stdio.h>

void func()
{
	static int a;
	a = a + 1;
	printf("a: %d\n", a);
}
int main()
{
	func();
	func();
	func();

	return 0;
}

결과:

a: 1
a: 2
a: 3
계속하려면 아무 키나 누르십시오 . . .


코드를 살펴보면, 배우지도 않은 함수가 쓰였는데 지금은 간단히 func 함수도 코드를 묶어놓은 하나의 특수한 '영역'으로 알아두도록 합시다. 그리고 func(); 와 같은 코드는 그 func 영역을 호출하는 것으로써, func 함수 내로 진입한다라고 간단히 알아둡시다.


먼저 메인 함수 내부를 보시면, func 함수가 세번이나 호출이 되고 있는데 첫번째 호출로 인해 func 함수 내부로 진입하게 되어, a 변수에 1을 더한 값이 a 변수에 들어가고 그 a의 값을 출력합니다. (정적 변수가 아닌 지역 변수였다면, 해당 선언된 영역을 벗어나면 알아서 소멸하므로 값이 유지되지 않습니다.) 그리고 두번째 호출도 마찬가지로 a에 1을 더하고, 그 값이 a에 들어가고 그 a의 값을 출력하는데 여기서 중요한 것은 두번째 호출의 결과에서 a가 1이 아닌 2가 출력되었다는 점입니다. 세번째 호출도 마찬가지로 값이 유지가 되면서 증가하고 있다는 사실을 알 수 있습니다. (그리고 정적 변수도 마찬가지로 우리가 초기화 하지 않으면 0으로 초기화 됩니다.)


잘 이해가 가지 않으신다면, func 함수 내의 static 키워드를 제외하고 컴파일 하여 결과를 다시 확인해보시기 바랍니다. 어떠한 차이가 있는지 살펴보시면, static 키워드가 어떠한 역할을 하는지 대충 짐작할 수 있습니다.

이번엔 외부 변수에 대해서 알아보도록 합시다. 이 외부 변수(extern variable)는 모듈별 분할 컴파일에 사용되는 녀석으로, 소스 밖에서도 사용 가능하며 함수 바깥에서 선언되었다면 어느곳에서라도 사용 가능한 전역 변수가 됩니다. 외부 변수는 extern라는 키워드를 붙이고 선언을 하는데 이 외부 변수는 선언 이전에 나온 함수에서는 참조할수 없으며 정적 변수처럼 자동으로 0으로 초기화 됩니다. 


간략히 정리하면, a란 변수에 extern란 키워드를 붙이게 되면 외부 모듈 어딘가에 a란 변수가 있다는 말로 이해하여 외부 모듈에서 a란 변수를 찾는다는 겁니다. 


소스.c:

extern int a;

void func()
{
	a = 50;
}

소스1.c:

#include <stdio.h>

int a;
extern void func();

int main()
{
	printf("a: %d\n", a);
	func();
	printf("a: %d\n", a);

	return 0;
}

결과:

a: 0
a: 50
계속하려면 아무 키나 누르십시오 . . .


위의 코드에서 소스1.c 부터 살펴보시면, func란 함수를 외부(extern) 모듈에서 찾게 되고, 외부 모듈에서의 함수를 호출합니다.

소스.c를 살펴보시면, a란 변수를 외부 모듈에서 찾게 되고, 외부 모듈에서의 변수에 50이란 값을 대입합니다. 결과를 살펴보시면, 첫번째 출력에서는 a가 0이란 값이 그리고 func 함수가 호출되고 나서의 a는 50이란 값을 출력하고 있음을 아실 수 있습니다.


아직 당장은 외부 변수란게 이해가 되지 않으셔도 괜찮습니다. 분할 컴파일에 대해서는 추후 더 자세히 다룰 예정이며, 이 단원은 함수 단원을 넘기고 어느정도 적응하신 뒤에 다시 보셔도 괜찮습니다.