C 언어 | 구조체 선언 | 외부 레벨 선언 - extern
extern 기억 클래스 지정자를 사용하여 함수의 외부에서 선언된 전역 변수를 여러 개의 소스 파일로 공유하는 방법, 또는 static 기억 클래스 지정자를 사용하여 선언된 파일 내에서 사용되고, 다른 소스 파일에서는 볼 수 없도록하는 방법을 소개한다.
전역 변수에 대한 참조
“변수의 유효 범위“에서 설명한 바와 같이, 함수의 외부 공간을 외부 레벨이라고 하고, 외부 레벨에서 수행된 선언을 외부 레벨 선언이라고 한다. 반대로, 함수의 내부 레벨이라고 자동 변수 등의 선언을 내부 레벨 선언이라고 한다. 내부 레벨과는 달리 외부 레벨의 가시 공간은 전체 파일이다. 그 C 언어 소스 파일의 모든 함수에서 외부 레벨에서 선언된 변수 등에 액세스할 수 있다.
외부 레벨 선언의 가시성은 파일 범위(scope)로 간주한다. 즉, 외부 레벨에서 선언한 변수와 함수는 그 선언을 한 소스 파일의 어느 위치에서도 액세스할 수 있다는 것이다. 다만, 선언을 하기 전에 함수 등에서 액세스할 수 없다. 따라서 일반적으로 파일의 시작에 필요한 선언을 하게 된다. 함수는 항상 외부 레벨 선언이고, 함수 내부에서 함수를 선언할 수 없다.
선언되기 전 위치에서 변수나 함수를 사용할 수 없다는 것은, 예를 들면 다음과 같은 경우이다.
void Function() {
printf("%s\n" , str);
}
char *str;
이 경우 Function() 함수에서 str 변수를 참조하고 있지만, str 변수는 Function() 함수보다 뒤에 선언되어 있다. 따라서 Function() 함수에서는 str 변수를 인식할 수 없다. str은 정의되지 않은 심볼로 해석되어 버리는 것이다. 함수이면 이러한 문제는 프로토타입의 선언으로 극복할 수 있었다.
선언의 위치가 불명확한 외부 레벨의 변수에 액세스하려면, 기억 클래스 지정자의 일종인 extern 지정자를 사용한다. 이 기억 클래스를 갖는 변수는 프로그램을 구성하는 C 언어 소스 파일 중 하나에서 외부 레벨에서 정의된 동명의 변수에 대한 참조이다. 즉, 함수 선언자의 변수 버전과 같은 것이라고 생각하면 된다. extern으로 선언된 변수는 그 시점에는 메모리를 확보(초기화)하지 않지만, 소스 어딘가에 동명의 외부 레벨 변수가 존재하는 것을 컴파일러에 통지한다.
extern 지정자
extern 형 변수명 ...
extern 지정자를 갖는 변수 선언은 초기화되지 않는다. extern으로 선언된 변수는 다른 곳에서 실체가 정의되어 있는지를 나타낸다.
또한 외부 레벨 선언으로 auto와 register를 사용할 수 없다. 외부 레벨 선언은 그 성질상 자동 변수가 될 수도 없고, 장기적으로 CPU의 레지스터를 차지할 수도 어렵다고 생각되기 때문이다.
코드1
#include <stdio.h>
extern char *str;
int main() {
printf("%s\n" , str);
return 0;
}
char *str = "Kitty on your lap";
코드1에는 파일의 시작 부분에 extern 기억 클래스를 가진 변수 str을 선언하고 있다. str 변수의 실체는 main() 함수보다 뒤에 초기화되어 있지만, extern 의해 참조가 생성되어 있기 때문에, main() 함수에서 str 변수를 참조하는 것이 허용되고 있다. 이 프로그램은 문제없이 컴파일할 수 있을 것이다.
extern 기억 클래스 지정자는 내부 레벨의 변수로도 지정할 수 있다. 이 경우 변수가 선언된 블록에서 지정된 외부 레벨에 대한 참조를 사용할 수 있다. 당연히, 블록 외부에서 extern을 지정한 변수가 보이지 않기 때문에 참조는 사용할 수 없다.
코드2
#include <stdio.h>
int main() {
{
extern char *str;
printf("%s\n" , str); /* OK */
}
/*printf("%s\n" , str); /* error */
return 0;
}
char *str = "Kitty on your lap";
코드2는 main() 함수 내에서 중첩된 익명의 블록으로 extern 기억 클래스를 가진 변수 str을 선언하고 있다. 블록 내에서 이 extern을 지정한 str에 대한 참조를 사용할 수 있다. 블록 밖에서는 str에 대한 참조를 유지하지 않기 때문에 main() 함수의 주석 처리된 printf() 함수를 컴파일하면 오류가 될 것이다. 어느 한 곳에 전역 변수를 시각화하고 싶은 경우에 유효하다.
정적 전역 변수
static 기억 클래스 지정자와 가진 글로벌 변수는 그 변수가 선언된 파일 내에서만 참조할 수 있다는 뜻이다. 사실 여러 C 언어 파일을 컴파일하면 다른 파일에서 extern 기억 클래스를 사용하여 전역 변수로 참조 할 수 있다. 예를 들어 다음과 같은 경우이다.
코드3
int iValue = 0xFF;
void Function(void);
int main() {
Function();
return 0;
}
코드4
#include <stdio.h>
extern int iValue;
void Function() {
printf("iValue = %d\n" , iValue);
}
코드3과 코드4를 동시에 컴파일하는 경우, 코드4는 iValue 변수를 extern 기억 클래스 지정자를 사용하여 선언하고, 코드3의 iValue 변수에 참조하고 있다. 여러 파일을 컴파일하는 방법은 사용하는 컴파일러의 도움말을 참조한다. Boland C ++ Compiler에서 여러 개의 소스 파일을 컴파일하려면 하나 이상의 공백으로 구분된 파일 이름을 지정한오.
BCC32 test1.c test2.c
다른 파일의 특정 전역 변수에 액세스하려면 extern을 사용하면 해결할 수 있지만, 경우에 따라서는 이것이 바람직하지 않은 경우도 있다. 어느 정도의 신뢰성이 필요한 글로벌 변수와 해당 파일의 함수 사이의 정보 전달에만 사용 목적 전역 변수라면, 다른 개발자가 만든 가시성이 있는 다른 소스 파일에서 액세스할 수 있는 것은 바람직하지 않다.
따라서 특히 공개가 필요없는 글로벌 변수는 static 기억 클래스 지정자를 설정하고 외부 파일에서 액세스 할 수 없도록 구조이다. 이렇게 함으로써 식별자의 충돌 등, 불필요한 오류와 오해에 의한 버그를 피할 수 있다.
코드5
#include <stdio.h>
static int iValue = 0xFF;
int main() {
printf("iValue = %d\n" , iValue);
return 0;
}
코드5의 iValue 변수는 static 기억 클래스와 글로벌 변수이다. 이 변수는 이 파일에서만 참조할 수 있으며, 외부 파일에서 extern 등을 사용하여 참조할 수 없다. 이렇게 함으로써 식별자의 예기치 않은 충돌을 피할 수 있다. 코드5와 코드4를 동시에 컴파일해도, 코드4에서는 iValue를 참조할 수 없기 때문에 오류가 발생할 것이다.