C 언어 | 함수 | 변수의 유효 범위

변수의 유효범위에 대해 설명한다. 변수의 범위는 선언된 함수 내에서만 유효한 자동 변수와 응용 프로그램이 실행되는 동안 항상 유효한 외부 변수에 따라 달라진다.

자동 변수

변수는 사용 가능한 범위 즉, 수명과 같은 것이 존재한다. 변수의 수명을 결정하는 것은 기억 클래스(Storage Class)라는 변수의 특성이다. 복합문의 내부에서 선언된 변수는 프로그램이 복합 문장에서 벗어날 때에 자동으로 해제되는 구조로 되어 있다. 이러한 변수를 자동 변수라고 한다 (또는, 그 국소성에서 로컬 변수라고도 한다).

변수의 기억 클래스는 변수의 가시성이 생긴다. 변수의 가시성은 그 위치에서 변수에 액세스할 수 있는지하는 것이다. 예를 들어, 다음과 같은 프로그램은 오류가 발생한다. 이유는 main() 함수에서 iValue가 보이지 않기 때문이다.

void Function() {
 int iValue;
}
int main() {
  iValue = 10;
  ...

Function() 함수에서 분명히 iValue 변수를 선언하고 있지만, 이것은 자동 변수이므로 main() 함수에서 액세스할 수 없다. 자동 변수은 그것이 선언된 블록이 실행될 때 만들어고, 블록을 벗어날 때에 삭제된다. 즉, 변수가 선언된 블록 밖에서는 그 변수는 사용할 수 없다는 것이다.

반대로 생각하면 다른 블록의 변수는 보이지 않기 때문에 다음과 같이 여러 블록에서 변수 이름이 중복되어도 프로그램은 문제 없다.

void Function() {
  int iValue;
}
int main() {
  int iValue;
 ...

식별자는 항상 고유해야 하며 같은 이름이 있으면 오류가 발생한다. 위의 코드는 iValue 변수의 이름이 충돌될 거처럼 느껴지지만, Function() 함수 iValue는 main() 함수에서 보이지 않고, 마찬가지로 main() 함수의 iValue는 Function() 함수에서 보이지 않기 때문에 문제가 없다. 이러한 자동 변수는 함수 블록 내에서만 유효하기 때문이다.

여기에서 중요한 것은 변수의 수명은 함수 단위가 아닌 복합 문장의 블록이라는 것이다. C 언어 초보자중에는 “함수 내의 변수는 선언된 함수 내에서만 유효하다"고 기억하는 사람도 있지만, 그렇지 않고 “자동 변수가 선언된 블록 중간에서만 유효하다"고 표현하는 것이 옳다고 말할 수 있다. 이는 다음의 프로그램에서 이해할 수있을 것이다.

코드1

#include <stdio.h>

int main() {
 {
   int iCount;
   for(iCount = 0 ; iCount < 10 ; iCount++)
      printf("1st for : iCount = %d\n" , iCount);
  }
 {
   int iCount;
   for(iCount = 0 ; iCount < 10 ; iCount++)
      printf("2st for : iCount = %d\n" , iCount);
  }
 /*printf("iCount = %d" , iCount); /*에러*/
  return 0;
}

이 프로그램은 main() 함수에서 두 개의 복합 문에 의한 블록을 형성하고 있다. 중요한 것은 두 블록 내부에서 같은 이름의 변수 iCount을 선언하고 있다. 그러나 이는 오류가 발생하지 않는다. 블록 안에서 선언 된 변수는 블록 밖에서는 보이지 않기 때문이다.

마지막에 printf() 함수의 주석을 풀어서 컴파일하면 오류가 발생한다. 이 점에서도 블록 밖에서는 블록의 내부 변수에 액세스할 수 없다는 것을 확인할 수 있다.

외부 변수

변수는 반드시 복합문 내부에 선언해야 한다는 것은 아니다. 함수 외부에서 변수를 선언할 수 있다. 이와 같은 위치에서 선언을 ‘외부적’이라고 표현한다. 예를 들어, 함수 자신은 항상 외부적이다. 함수는 어떤 블록 내부에 배치되는 것이 아니라 어디서든 호출할 수 있는 광역적인 존재이기 때문이다.

함수 밖에서 선언된 변수를 외부 변수라고 한다(또는, 그 광역성에서 전역 및 글로벌 변수라고 한다). 내부 변수와의 가장 큰 차이점은 모든 함수에서 액세스할 수 있으며, 수명이 영구적이라는 점이다. 외부 변수는 프로그램이 종료될 때까지 유효하다. 외부 변수는 프로그램 전체에서 공유하고 싶은 정보를 저장하는데 적합하다.

이와 같이, 변수는 선언 위치에서 기억 클래스와 가시성에 영향을 준다. 함수의 외부에 있는 선언을 외부 레벨이라고 하고, 함수내의 선언을 내부 레벨이라고 표현한다. 이러한 차이에서 변수의 가시성 및 수명이 다르기 때문에 변수의 역할에 따라 구분하고 구분한다.

코드2

#include <stdio.h>

int iValue = 10;
void Function(void);

int main() {
 Function();
 iValue = 100;
 Function();
 return 0;
}

void Function() {
 printf("iValue = %d\n" , iValue);
}

코드2는 외부 변수 iValue를 선언하고 있다. 이 변수는 main() 함수에서도 Function() 함수에서 액세스 할 수 있다. 프로그램을 실행하면 main() 함수에서 iValue의 값을 변경하고, Function() 함수에서 iValue를 표시하고 있다. 이것으로 같은 변수를 조작하고 있는 것을 확인할 수 있다.

다만, 외부 변수는 어떤 함수에서도 자유롭게 값을 변경할 수 있기 때문에 대규모 프로그램이 되면 어디에서 어떤 함수가 어떤 값을 대입하는지 알수가 없게 되거나 버그와 보기 어려운 코드를 만들어 버리는 원인이 될 수 있다. 일반적으로 인수 또는 반환 값을 사용하여 함수간에 정보를 전달하여야 하며, 특별한 사유가 없다면 외부 변수는 최대한 피해야 한다.

외부 변수와 자동 변수의 식별자가 충돌하면, 로컬 변수가 우선시 된다. 이 경우는 컴파일 오류가 발생하지 않는다.

코드3

#include <stdio.h>

int iValue = 1;

int main() {
 int iValue = 10;
  {
   int iValue = 100;
   printf("iValue = %d\n" , iValue);
  }
 printf("iValue = %d\n" , iValue);
  return 0;
}

이 프로그램은 외부 변수의 iValue 변수와 main() 함수 내에 iValue 변수, 그리고 블록의 iValue 변수 이렇게 3가지 변수가 선언되어 있다. 이들은 동일한 이름을 가지고 있지만, 선언 위치가 다르기 때문에, 기억 클래스가 다르다. 실행 결과에서 printf() 함수의 인수로 지정하고 있는 iValue 변수는 보다 가까운 로컬(내부) 변수가 우선되는 것을 확인할 수 있다.