지역변수(Local Variables)의 개념이 좀 낯설다.
변수가 뭔지는 이제 알겠는데 지역변수는 무엇일까?
한글로 지역 변수라 번역했지만 영어로 local variable이다.
구글 사전 정의에 따르면 local은...
-> belonging or relating to a particular area or neighborhood, typically exclusively so.
특정한 지역이나 이웃에 속하거나 관련된 것이다.
혹시 지역변수란 이름이 이상하게 생각하는 사람이 있을지 모르겠으나 필자는 이상하다고 생각했다. local 의 사전적 정의는 <특정 지역에 소속된> 이 제대로된 의미로 볼 수 있다.
'지역' 에는 소속에 대한 부분이 빠져있다. 혹시 일본에서 건너온 개념이 아닐까 싶어서 일본의 용어를 찾아봤다.
우리나라는 과거 건설등 공학용어를 일본식을 차용한게 많다.
그랬더니 꽤 흥미로운 사실을 알게되었다. 사실 필자는 IT관련하여 거의 영어로 검색을 해서 일본어로 검색한 적이 없다.
일본에서는 지역변수와 전역변수를 다음과 같이 표기한다.
もう一度基礎からC言語 第34回 変数の通用範囲~自動変数と静的変数/局所変数と広域変数 局所変数と広域変数 (grapecity.co.jp)
가타카나 표기로 로-카루, 그로-바루 라고도 통용된다. 현재는 양쪽을 다 사용하는 것 같다.
국소라는 것은 국소 마취같은 단어에 쓰는 전체의 어느 한 부위를 말한다. 광역은 전역과 별로 큰 차이가 없어 보인다.
그러니까 한국에 IT용어가 들어올때 일본과는 전혀 상관없이 한국인이 독자적으로 번역한 것으로 보인다. 하긴 일제시대에는 컴퓨터 자체가 없었기 때문이다.
한국 사람이 번역한 것은 다행이긴 한데 지역변수란 이름은 썩 잘 지었다고 생각하지 않는다.
로컬변수란 것은 한정적인 의미가 들어가 있으면 이해하기가 쉬운데 지역변수에는 그런 의미가 생략되 있다. 어느 지역인지 모르겠지만 어딘가 구분이 되있는 것이다. 라는 것이고 반면 일본인들이 지은 국소변수라는 말에는 전체의 한정적인 심상이 떠오른다. 즉 국소변수가 있으면 광역변수도 당연히 있을 것이다. 영어의 local 과 global 을 잘 표현하는 단어이다.
사실 그들도 그 한자어가 일상어도 아니니까 로컬이란 표기를 더 많이 쓰는 것 같다. 구글 검색결과를 보면 알 수 있다. 아무리 번역을 잘해도 본고장의 의미 전달을 100% 하는게 어렵다는 것은 외국어를 배운 사람은 알 수 있다. 마치 C언어를 배운 사람이 기계의 사고방식을 컴퓨터를 모르는 사람에게 설명하기 어려운 것과 같다.
어쨋든 이 지역변수라는 단어를 하나 알아보기위해 멀리 돌아왔다. 용어를 아는게 중요하기 때문이다. 지역변수의 개념을 잡으면 함수뿐 아니라 클래스, 접근제어자, 스택 메모리, 힙 메모리 등을 컴퓨터의 구조를 이해하는데 도움이 된다. 모든 것은 얼기설기 연결되어 있다. C++ 언어는 하나라도 대충할 수 있는 내용이 없다. 뒤로 가서 막히면 결국 다시 앞부분으로 와야한다.
먼저 첫번째 지역변수는 main 함수의 지역변수이다.
#include<iostream>
int main(int argc, char const *argv[])
{
int a;
int b;
a = 10;
b = 7;
return 0;
}
이 코드에서 main 함수 지역에 a와 b를 선언한다. a에는 값 10을 b 에는 값 7을 할당했다.
main 의 지역변수 a, b는 { } 괄호 표시를 벗어나면 사용할 수 없다.
따라서 아래와 같은 코드는 오류가 발생한다.
#include<iostream>
void printVariables();
int main(int argc, char const *argv[])
{
int a;
int b;
a = 10;
b = 7;
return 0;
}
void printVariables(){
std::cout << "a = " << a << "\n";
std::cout << "b = " << b << "\n";
}
오류 메시지는 명확하다. "a와 b는 이 범위(scope) 에 선언되지 않았습니다."
main 지역(scope)에서 선언되지 않은 변수는 다른 함수 (printVariables) 의 지역(scope)에서 사용할 수 없다. 그 이유는 a와 b 가 main 함수에 속한 local 변수이기 때문이다.
여기서 처음에 말하려던 것 처럼 main 함수에 속한 지역변수라는 문장보다 main 함수에 속한 로컬변수라는 말이 더 어울린다. main 이나 printVariables 나 둘다 지역이기 때문이다. 지역이 아니라 local (좀더 범위가 좁혀진 특정의 지역) 이기 때문이다.
지역변수(로컬변수)는 특정 지역에 속해있다는 부분이 핵심이다. main 함수 조차 특정지역에 놓여져 있다. main 함수로 부터 시작하지만 사용자가 제작한 함수도 대등한 관계다.
std::cout 이란 것도 언뜻 함수로 보이는데 이는 함수가 아니라 객체다. 객체에 대해서는 뒤에 가서 알게되겠지만 이들도 지역변수라는 개념이 변형되는 것이니까. 어떻게 보면 클래스는 지역을 좀 더 추상적으로 다루는 방법이다. C언어의 printf 함수라면 좀 더 설명이 쉽겠지만 C++ 이 C언어 보다 한단계 진보하고 어렵다. ++ (증감연산자)를 괜히 붙인게 아니다.
global variable, 광역변수, 글로벌 변수라고 하는 전역변수다. 지역변수가 어떤 특정한 지역에 소속한다면 모든 지역에서 사용할 수 있는 변수를 만들 수 있지 않을까? 지역에 관계없이 사용한다면 분명한 이점이 있을 것 같다.
전역변수를 사용해본다.
#include<iostream>
#define LineSeparator std::cout << "------------ [Line] ------------\n"
void printVariables();
int a;
int b;
int main(int argc, char const *argv[])
{
a = 10;
b = 7;
LineSeparator;
std::cout << "main 함수 a = " << a << "\n";
std::cout << "main 함수 b = " << b << "\n";
LineSeparator;
printVariables();
LineSeparator;
std::cout << "main 함수 a = " << a << "\n";
std::cout << "main 함수 b = " << b << "\n";
return 0;
}
void printVariables(){
a = 999;
b = 777;
std::cout << "print 함수 a = " << a << "\n";
std::cout << "print 함수 b = " << b << "\n";
}
print 함수를 호출하기 전과 후에 값이 달라진다. 전역변수는 최초의 괄호 { } 즉 main 함수의 바깥에 선언해서 사용할 수 있다. 주의할 점은 main 보다 위에 선언해야 한다. 그 아래 선언하면 컴파일러는 찾지 못하고 오류를 낼 것이다. 좀 바보같지만 컴파일러가 하는일은 위에서 부터 순차적으로 코드를 분석하게 되어있다. 코드를 분석하기 전에 사전작업도 하는데(전처리) main 함수 아래에 전역변수가 선언되었는지 같은 사소한 문제도 관찰하지 않는다. 사람은 길지 않은 소스코드를 보면 그 정도는 쉽게 안다. 컴파일러는 정해진 일만 처리하기 때문에 모른다.
언어를 배울 때 겪는 수많은 컴파일 오류들은 컴파일러의 동작도 사람과 비슷할 거라고 착각하기 때문인 경우가 많다. 이런 차이를 잘 알면 좀 더 수월하게 코딩을 할 수 있다.
지역변수는 { } 중괄호를 사용해 함수 안에 같은 이름으로 선언할 수도 있다. 그런 경우 더 안쪽 지역 변수에게 우선권을 준다. 다음과 같이 계속 지역변수를 만들어 사용할 수 있다. 아래의 지역변수 선언은 C++에서 문법적으로 완전한 코드이다.
이런 생각도 해볼 수 있다. 왜 저렇게 일을 복잡하게 하지? 그냥 모든 변수를 전역변수에 넣어놓고 할 수도 있지않나?
-> 가능하다. 그런데 생각보다 우리가 사용하는 컴퓨터 시스템은 불안정하다. 컴퓨터의 사양은 날이 갈수록 발달하고 있으나 그 시스템을 받치는 근본적 구조는 크게 변한게 없다. 대부분이 가정과 일터에서 사용하는 x86 아키텍쳐라는 00 86세대의 구조이다. 90년대 이전에 286을 사용했으니까...
수많은 엔지니어들의 경험으로 함수라는 하나의 모듈을 지역으로 설정하고 제한을 두어 사용하면 오류가 적고 시스템 자원의 사용을 최적화 할 수 있다는 것을 알았다. 함수는 스택구조에 데이터를 정의하는데 함수의 사용이 끝나면 즉시 스택을 반환하고 호출자에게 제어권을 반환한다. 전역변수는 프로그램의 시작부터 끝날 때까지 항상 메모리에 데이터가 유지되야 한다. 지역변수는 그럴 필요가 없다.
이렇게 지역을 한정하여 짧게 변수를 사용하는 것이 프로그램의 오류를 적게 하고 시스템 자원의 이용을 극대화 할수있기 때문이다. 아마 초기에는 전역변수를 사용하려고 해도 메모리 용량이 부족해서 어쩔 수 없이 지역변수 위주로 프로그램을 했는지도 모를일이다. 1950년대에 포트란이 있었으니까... 그 때 프로그램을 하셨던 분들은 이미 은퇴하셨을테니 컴퓨터의 초창기도 벌써 역사의 뒤안길로 사라지고 있다.
마지막으로 함수는 각각의 지역을 갖는다고 했는데, 함수는 서로를 호출할 수 있다는 것이다. 서로를 호출할 때 전달하고 받는 것이 매개변수와 반환값인데 이 부분은 함수의 매개변수를 포스트할 때 좀 더 자세히 다루기로 한다. 여기서는 함수는 다른 함수를 호출할 수 있다는 것이다.
제일 처음 나오는 main 함수는 누가 호출할까?
C++의 main 함수는 사용자가 프로그램을 실행시킬 때 운영체제가 프로그램 코드를 메모리에 올려놓으면서 처음으로 호출한다. 함수를 호출하면서 어떤 값을 전달할 수도 있고 반환값을 요구할 수도 있다. 호출된 이후에 운영체제는 컴퓨터구조의 계층에서 애플리케이션 영역(소프트웨어가 돌아가는 영역)에서 컴퓨터를 사용할 수 있도록 제어권을 넘겨준다. 즉 CPU를 통해서 메모리와 입출력 장치를 사용할 수 있도록 권한을 준다는 것이다. 모든 권한은 아니지만 CPU를 직접 사용할 수 있는 강력한 권한이다.
사용자의 프로그램을 실행시키면서도 여러 프로그램이 동시에 돌아가는 이유는 멀티태스킹 하기 때문인데 그것 까지는 이 포스팅의 범위를 벗어난다.
위의 예제 프로그램에서 main 프로그램이 사용자 정의 함수인 print... 함수를 호출했다. 그리고 호출이 끝나면 다시 main 으로 제어를 넘긴다. 이렇게 모든게 연결되어 있다는 것을 알 수 있다.
그렇다면 다른 함수도 main 함수를 호출할 수 있을까? 정답은 C++ 에서는 가능하다는 것이다. 컴파일러나 IDE 마다 다를 수는 있다. 아래와 같은 코드로 (int main(void) 경우) 메인함수를 호출할 수 있다.
* 윈도우 운영체제 -> main 함수 -> 사용자 정의함수 -> main 함수
과정은 위와 같다.
결과적으로 main 을 수행하면 정상적인 호출방법이 아니기 때문에 어떤일이 발생할지 모른다. 위험하니까 하지 않는게 좋다. 필자의 경우 main() 이라는 출력이 반복되면서 무한루프에 빠지고 비주얼 스튜디오 코드가 다운되었다.
요는 비정상이긴 하지만 main 도 호출할 수 있다는 것이다. 그것이 사용자 정의 함수와 서로 동등하다는 것을 말해준다.