1. 전역 변수의 개념

변수는 함수 외부에 선언될 수 있다. 이러한 변수를 전역 변수(global variables)라 부른다. 예를 들면, 다음 코드에서

g는 전역변수이다. 여러분은 fun() 내부에 선언된 n은 지역변수임을 알고 있다.

 

전역 변수는 지역 변수와는 달리, 이 변수가 선언된 이 후에 정의되는 모든 함수에서 별도의 선언 없이 사용될 수 있다. 따라서 전역 변수가 프로그램의 첫 부분에 선언된 경우에는, 프로그램 전체에 걸쳐서 사용 가능하다.

변수가 사용될 수 있는 프로그램의 영역을 변수의 영역(scope)이라 부른다. 따라서 지역 변수의 영역은 이 변수가 선언된 함수 내부가 되며, 전역 변수의 영역은 이 변수가 선언된 이후의 프로그램의 모든 영역에 해당된다.

 


앞서, 지역 변수는 함수의 호출시에 생성되며, 함수의 실행이 종료되면 사라진다고 설명하였다. 반면에, 전역 변수는 선언 이후부터 프로그램 종료시까지 존재한다. 즉, 전역 변수는 일단 생성되면, 프로그램과 함께 그 수명을 같이한다고 볼 수 있다.

다음 프로그램을 생각해보자.

 

프로그램의 첫 부분에 선언된 변수 a는 함수 외부에서 선언되었기 때문에 전역 변수이다. main()과 fun()은 각각 자신의 지역 변수 b가 선언되어 있다. 두 함수의 지역 변수가 이름이 같지만, 그 영역이 함수 내부에 제한되기 때문에 문제가 되지 않는다.

또한, fun()에서 a를 참조하고 있다. a가 fun()에서 선언되어 있지 않기 때문에 오류가 발생하는가? 그렇지 않다. a는 전역 변수이고, 따라서 그 영역은 프로그램 전체이고, 프로그램에 포함된 어떤 함수에서도 올바르게 참조 가능하다.

프로그램 15-2 실행 과정을 전역변수와 지역 변수간의 차이를 살펴보자. 프로그램 실행은 다음과 같이 4단계로 구분된다.

     
       
    main()에서 처음 두 번째 문장 scanf()까지 실행되었을 때를 생각해보자. 사용자가 다음과 같이 입력하였다고 가정한다.

다음은 함수 프레임을 보여준다. main()의 지역 변수 b는 main 프레임에 포함되나, 전역 변수 a는 함수 프레임에 포함되지 않고, 그 외부에 위치하는 것을 볼 수 있다. 이것은 전역변수가 특정 함수에 연관되지 않는다는 것을 보여주기 위함이다.


 
       

     
       
   

다음은 main()에서 fun()이 호출되었을 때의 함수 프레임을 보여준다. fun()의 지역 변수 b가 fun 프레임에 생성된다.

이름이 같은 지역 변수가 b가 각각 main 프레임과 fun 프레임에 존재하는 데, 지역변수의 영역이 함수 내부에 국한되기 때문에 아무런 혼동을 초래하지 않는다. 즉, main 프레임에 속한 b는 그 영역이 main() 내부에 국한되며, fun 프레임에 속한 b는 그 영역이 fun() 내부에 국한된다.

fun()의 첫 번째 문장은 a를 참조한다. a가 fun()에 선언되지 않았지만, 오류가 발생되지 않는다. a는 전역 변수이기 때문이다. 프로그램에 포함된 모든 함수는 앞서 선언된 전역 변수를 참조할 수 있다는 것을 기억하라.

 
       

     
       
   

다음은 fun()에서 처음 두 개의 배정문을 실행한 후에, 함수 프레임을 보여준다. a의 전역변수는 100으로 변경되고, fun()에 속한 b의 지역변수는 200으로 초기화되는 것을 알 수 있다.

 
       

     
       
   

다음은 fun()의 실행이 종료된 직후의 함수 프레임을 보여준다. fun()의 실행 종료로, fun 프레임은 사라지고, 단지 main 프레임만이 남는다. fun()이 호출되기 직전과 비교하면, a의 전역변수가 10에서 100으로 변경되었음을 알 수 있다.

 
       

 

 





2. 전역 변수의 사용 위험성

전역 변수를 프로그램 첫 부분에 선언하면, 프로그램의 어느 곳에서도 이 전역변수를 참조할 수 있다. 따라서 전역 변수를 사용하면 함수에 매개변수를 전달할 필요도 없어진다.

다음은 전역 변수를 사용한 함수간의 인터페이스를 보여준다.

 

함수 A가 B에 값을 어떻게 전달할 수 있는가? 이것은 함수 A는 전달하고자 하는 값을 전역 변수 g에 설정하고, 함수 B는 이 전역 변수를 참조함으로써 가능하다. 위의 그림에서 오른쪽 방향 화살표를 클릭하시오.

반대로, 함수 B가 호출자에게 값을 반환하는 것도 가능하다. 이것은 함수 B가 g에 값을 변경하고, 함수 A가 g를 참조함으로써 가능하다. 위의 그림에서 왼쪽 방향 화살표를 클릭하시오.

다음은 지역 변수를 통한 함수간의 인터페이스를 보여준다. 이것은 지금까지 여러분이 매개변수를 통해서 함수간의 인터페이스를 표현해온 방식이다.

 

프로그램 15-2는 전역 변수 a를 통해서 main()과 fun()의 두 함수간에 인터페이스를 표현하고 있음을 알 수 있다.

이 프로그램에서 a가 main()의 지역 변수인 경우에도, fun()의 실행 중에 a에 영향을 미치고자 한다면 어떻게 표현할 수 있는가?

여러분은 다음 프로그램과 같이 fun()에게 a를 매개변수로 전달하고, fun()으로부터 변경된 a를 반환받는 것을 생각할 수 있다.


프로그램 15-2와 15-2-1을 비교해보라. 프로그램 15-2가 작성하기 더 간편하고, 함수간에 데이터가 전달되지 않기 때문에 더 효율적이다.

그렇다면, 전역 변수를 사용하여 함수간의 인터페이스를 표현하는 것이 바람직한가?

변수에 잘못된 값이 포함되어 발생한 오류를 탐지하고 있다고 가정해보자. 만약, 이 변수가 전역 변수라면, 프로그램에 포함된 모든 함수가 이 변수를 참조할 수 있으며, 따라서 여러분은 오류를 탐지하기 위해서 프로그램 전체를 살펴보아야 할 것이다. 프로그램이 복잡하고 규모가 큰 경우에 이러한 디버깅 작업은 상당한 노력과 시일이 걸릴 것이다.

 

그러나 해당 변수가 지역 변수라면, 오류를 찾기 위해서 단지 그 변수를 포함하는 한 개의 함수만을 살펴보면 될 것이다.

 

앞서 살펴보았듯이, 전역 변수의 사용은 프로그램 오류 발생시에 그 원인을 찾아내는 과정이 매우 어렵다. 또한, 그 원인을 찾아냈다 하더라도 오류를 해결하는 과정도 어렵다. 왜냐하면, 전역 변수가 포함된 코드의 수정은 이 전역 변수를 참조하는 다른 모든 함수에 영향을 미치기 때문이다.

이러한 문제점을 해결하기 위해서, 전역 변수를 가급적으로 사용하지 않는 것이 바람직하다. 따라서 함수간의 인터페이스를 위해서 전역 변수보다는 매개변수를 사용하여 수행하는 것이 훨씬 더 나은 방법이다.

3. 예제 프로그램

전역 변수는 지역 변수와는 달리 함수 호출간에 값을 유지하며, 프로그램의 내부 상태(internal state)를 표현하는데 적합하게 사용될 수 있다. 전역 변수를 유용하게 사용할 수 있는 다음 문제를 생각해보자.

 
   
문제
   
  사용자로부터 한 줄의 문자열을 입력받고, 문자열에 포함된 단어들을 모두 찾아내어 출력하시오. 단, 사용자가 입력한 문자열은 알파벳과 숫자들로 구성된다고 가정한다.  
     

 
   
분석
   
 


해결해야 할 문제가 무엇인지 정확하게 파악되어야 한다. 사용자가 입력한 다음의 문자열을 생각해보자.



문자열에 포함된 단어는 공백으로 구분된다. 따라서 위의 문자열에 포함된 단어는 다음과 같이 모두 4개임을 알 수 있다.

 

문제는 사용자가 입력한 문자열로부터 모든 단어들을 찾아내어 다음과 같이 출력할 것을 요구한다.

 


문제를 정확하게 파악한 후에, 다음과 같이 분석한다.

 
분석사항
          내용
 
문제입력
     . 문자열(char line[])
 
문제출력
     . line에 포함된 단어들(char word[])
 
관계식
     . line의 길이 ≤ ∑ word의 길이

 
     

 
   
알고리즘
   
 

line에 포함된 문자열로부터 단어 word를 순서대로 하나씩 가져오는 것이 필요하다. 따라서 위 문제에 대한 알고리즘을 다음과 같이 개략적으로 작성할 수 있다.

위의 알고리즘에서, 단계 2와 3은 복잡한 수준이다. line의 문자열로부터 단어를 어떻게 식별하여 가져올 것인가? 또한, line으로부터 아직 가져오지 않은 단어가 남아 있는지를 어떻게 알 수 있는가? 이 두 가지 사항에 대해서 생각해야 한다.

먼저, line에 포함된 단어를 가져오는 방법을 생각해보자. 단어는 첫 번째 문자부터 공백이전까지의 문자들로 구성된다. 따라서 다음과 같이 문자열로부터 단어를 식별할 수 있다.

 


여기서 cp는 line에서 다음에 읽어들일 문자의 위치를 가리킨다. 먼저, cp는 line의 첫 번째 문자를 가리킨다. word는 line으로부터 가져온 단어를 포함하기 위한 문자 배열이다.

cp를 이동시켜가면서, cp가 가리키는 문자를 word에 가져온다. cp가 가리키는 문자가 공백이면, line으로부터 한 개의 단어가 식별된 것이다. 이 단어에 포함된 모든 문자들은 현재 word에 포함되어 있다. 이때 word에 포함된 문자들을 C의 문자열로 생성하기 위해서, 널 문자를 추가하는 것이 필요하다.

다음은 line으로부터 첫 번째 단어를 식별하여 가져온 후의 상태를 보여준다. cp가 다음번째 단어의 첫 번째 문자를 가리키는 것을 유의하라. 따라서 다음번째 단어의 식별 과정은 ‘E'의 문자부터 시작되고, 그 결과로 “Ending"이 식별될 것이다. 이것은 우리가 원하는 것이다.

 

line의 문자열로부터 3번째 단어 “Story"가 식별된 후에, cp는 line의 끝을 가리키게 될 것이다. 이제 line의 문자열로부터 더 이상 검색할 문자가 존재하지 않는다. 따라서 line에는 찾을 단어가 더 이상 남아 있지 않는다.

이러한 사실로부터, cp가 line 문자열의 끝에 도달하였는지의 여부를 알고리즘 15-3의 while 문의 반복 조건으로 표현할 수 있겠다.

이러한 생각을 토대로, 알고리즘 15-3에 대해서 다음과 같이 main() 함수를 개략적으로 작성할 수 있다.

 


위의 main() 함수는 아직 완성 상태가 아니다. 즉, line이나 word의 변수 등이 선언되어 있지 않다. 먼저, 함수 EndOfLine()과 getNextWord()의 동작에 대해서 생각해보자.

getNextWord()는 호출시마다 다음번째 단어를 식별하여 반환한다. 이를 위해서, 함수 호출간에 앞서 살펴본 cp처럼 다음번째 검사할 문자의 위치를 추적하는 것이 중요하다.

예를 들면, 앞의 예제에서 getNextWord()가 처음 호출되었을 때는 ‘H'의 문자부터 검사되나, 두 번째 호출되었을 때는 ’E'의 문자부터 검사되어야 한다. getNextWord()는 이러한 사실을 cp 변수 값으로부터 알 수 있다.

cp처럼 함수의 호출간에 유지되어야 하는 정보를 내부 상태(internal state)라 부른다. 이러한 정보는 함수의 지역 변수로 표현될 수 없다. 왜냐하면, 지역 변수는 함수 실행이 종료되면 사라지기 때문이다. 따라서 내부 상태 정보는 프로그램 실행 종료시까지 수명이 유지되는 전역 변수로 표현하는 것이 효과적이다.

다음은 위의 두 함수에 대한 알고리즘을 개략적으로 기술한다.

다음은 main(), EndOfLine(), getNextWord()의 함수들간의 관계를 보여주는 함수 관계 다이어그램이다.

 


위의 함수 관계 다이어그램에서 main()에서 함수 호출시에 line과 cp의 데이터가 전달되는 것을 알 수 있다. cp는 함수 호출간에 유지되어야 하는 내부 상태를 표현하므로, 전역변수로 표현되어야 한다. line도 두 함수에서 변경 없이 사용되므로 전역 변수로 표현한다.

 
     

 
   
구현
   
  문제에 대해서 설계한 알고리즘을 토대로 다음과 같이 프로그램을 작성한다.


위의 프로그램에서 line, cp, lineLen이 전역 변수로 선언되어 사용되고 있음을 유의하라. lineLen은 line에 포함된 문자열의 크기를 나타내며, 각각 getNextWord()와 EndOfLine()에서 참조되고 있다.
 
     

 



다음 사항을 반영하도록 프로그램 15-4를 수정하고 실행시키시오.

1. line에 포함된 단어의 평균 길이를 구하여 출력하시오.
2. line에 포함된 단어들 중에서 모음으로 시작된 단어의 비율을 구하여 출력하시오.

 



top으로... 다음페이지로.. 이전페이지로.. home으로..