본문 바로가기
프로그래밍/C || CPP

c언어 범위 별 변수 차이를 알아보자 (identifier scope)

by _BlankSpace 2019. 7. 17.

c언어를 하다보면, 분명히 위에서 num이라는 변수의 값은 5였는데, 어떤 부분에서는 값이 달라져서 프로그램이 꼬이는 경우가 생기는 경우가 있습니다. 보통, 이러한 경우는 같은 변수명을 사용할 경우에 종종 발생합니다.

 

하지만, 어떤 경우에는 같은 변수명을 써도 값이 달라지지 않는 경우도 있습니다. 그 이유는 c언어는 범위에 따라 같은 변수를 사용해도 다르게 인식하도록 설계되어 있기 때문입니다.

 

이번 포스팅은 범위 별로 변수를 어떻게 사용하는 지에 따라 같은 변수명을 사용해도 다르게 인식하는 케이스를 정리하려고 합니다.

 

c언어의 범위(Scope)

서론에서 말했듯이, c언어는 범위(scope) 안에서 같은 변수명이라도, 다른 변수로 인식하도록 설계되어 있습니다.

 

크게, c언어는 4가지 정도의 범위 종류로 나눌 수 있다고 합니다.

- 블록 범위 (block scope)

- 파일 범위 (file scope)

- 함수 범위 (function scope)

- 함수 프로토타입 범위 (function prototype scope)

 

이번 포스팅에서는 위의 4가지를 중점으로, 각각 케이스를 바탕으로 설명하도록 하겠습니다.

 

그럼 바로 설명으로 들어가도록 하겠습니다!

 

네스팅된 범위 (Nested scopes) 를 알아보자.

Nested scopes를 어떻게 번역해야 하나 고민을 많이 하다가 그냥 그대로 적기로 했습니다. (너무 날(?) 인가요?)

 

이 케이스는 위에서 말한 4가지 종류에 포함되지는 않지만, 많이 보셨을만한 케이스라고 할 수 있습니다.

Nested scopes는 같은 네임스페이스에서 같은 범위안에 같은 식별자로 이름 지어졌지만, 다른 엔티티로 구분되는 경우를 말합니다.

글로 설명하기에는 꽤 힘들기 때문에, 아래 예제를 보면서 설명하도록 하겠습니다.

  1 #include <stdio.h>
  2 
  3 int num;
  4 
  5 void tempFn(void) {
  6     int num = 1;
  7     {
  8         int num = 10;
  9         num++;
 10         printf("inner num = %d\n", num); // 11
 11     }
 12     num++;
 13     printf("Outer num = %d\n", num); // 2
 14 }
 15 
 16 int main(void) {
 17     int num = 20;
 18     tempFn();
 19     num++;
 20     printf("main num = %d\n", num); // 21
 21     return 0;
 22 }

6 ~ 11줄을 보면, { } 로 묶인 것을 볼 수 있습니다. 이러한 구조를 nested scopes라고 부릅니다. 보통, 개별의 동작을 기대하는 경우에 이처럼 내부 블록으로 묶어서 코딩을 합니다.

 

위 예제에서 특이한 점은, 3번, 6번, 8번, 17번 줄에서 num을 각각 선언하였습니다. 하지만, 실제로 출력하면, 서로 다른 값을 출력하고 있다는 것을 알 수 있습니다.

 

이처럼, 3번 줄에서 전역 변수로 num을 선언하여도, 각각 지역변수로 num을 선언한다면, 각각의 엔티티로 인식한다는 것입니다.

 

이처럼, 범위에 따라서 같은 변수명이라도, 다른 엔티티로 구별한다는 것을 알 수 있습니다.

 

 

블록 범위 (Block scope) 를 알아보자.

이 범위는 함수 정의 내부의 표현문(compound statement, expression, declaration) 에서 선언되는 식별자의 범위를 말합니다.

일단은 표현문이라고 적었는데, 예를 들자면 if, switch, for, while, do-while과 같은 것의 범위를 말합니다.

 

역시나, 글로 설명하는 것은 굉장히 어렵기 때문에 예제를 통해서 설명을 하도록 하겠습니다.

  1 #include <stdio.h>
  2 
  3 void tempFn(int num)
  4 {
  5     num++;
  6 
  7     if (3 > 0) {
  8         int num = 5;
  9         printf("num = %d\n", num); // 5
 10     }
 11 
 12     for (int num = 0; num < 3; num++) {
 13         printf("num = %d ", num); // num = 0, num = 1, num = 2
 14     }
 15 
 16     printf("\n");
 17     printf("num = %d", num); // num 1
 18 }
 19 
 20 int main(void) {
 21     int num = 0;
 22     tempFn(num);
 23 }

3번 줄에서 tempFn라는 함수의 인자로 num 값을 받습니다.

이후, 8번 줄과 12번 줄에서 num을 새로 정의하였고, 9번, 13번 줄에서 사용해도 각각 다른 num 값이라는 것을 알 수 있습니다.

 

이처럼, 블록 범위(block scope)에서의 식별자도 서로 다르게 인식한다는 것을 알 수 있습니다.

 

파일 범위(File scope)를 알아보자.

일단, 파일 범위라는 말의 뜻을 알아야 합니다. 파일 범위라고 해서 파일안에서의 범위인가라고 인식할 수 있는데, 이것은 어떤 블락의 외부 또는 함수의 인자로 선언된 식별자의 범위를 말합니다.

 

바로, 예제를 통해서 설명을 하도록 하겠습니다.

  1 #include <stdio.h>
  2 
  3 int num; // 외부에서 선언한 num
  4 static int externFn(int num) // 함수의 인자로 선언한 num
  5 {
  6     num++;
  7     return num;
  8 }
  9 
 10 int main(void) {
 11     num = externFn(5);
 12     printf("%d", num); // 6
 13 }

3번과 4번 줄을 보면, num을 선언하였습니다. 3번 줄은 외부에서 선언한 num이고, 4번 줄은 externFn이라는 함수의 인자로 선언한 num 값입니다. 이러한 케이스를 파일 범위라고 부릅니다.

 

이것 역시, num 값은 각각 다른 엔티티로 인식하여, 서로 다른 값을 출력하게 됩니다.

 

함수 범위 (Function scope)를 알아보자.

이것은 함수 내 어디든지 함수 또는 라벨을 사용하는 경우를 말합니다.

다시 말하면, 우리가 어떠한 함수를 정의했다고 하면, 그 함수를 정의한 순간, 아래에는 아무대나 함수를 사용할 수 있습니다.

 

역시나, 말로 표현한 것은 이해하기 힘드시죠? 바로 예제를 보시며 이해해보세요!

  1 #include <stdio.h>
  2 
  3 void printHello(int num)
  4 {
  5     printf("Hello %d\n", num);
  6 }
  7 
  8 void fn1()
  9 {
 10     printHello(1); // Hello 1
 11     {
 12         printHello(2); // Hello 2
 13     }
 14 }
 15 
 16 void fn2()
 17 {
 18     printHello(3); // Hello 3
 19 }
 20 
 21 int main(void)
 22 {
 23     fn1();
 24     fn2();
 25     return 0;
 26 }

먼저, 3번 줄에서 printHello라는 함수를 정의합니다. 그럼, printHello 함수를 아래부분에서는 아무대나 사용할 수 있습니다.

 

그 예로, 10, 12, 18번 줄에서 사용한 것을 볼 수 있습니다. 이러한 사용 범위를 함수 범위 (Function scope)라고 부릅니다.

 

함수 프로토타입 범위 (Fuction prototype scope) 을 알아보자.

이것은 함수의 인자를 설정할 때, 앞의 인자를 이용하여 뒤에 인자의 범위를 설정하는 것을 말합니다. 예를 들면, 아래와 같습니다.

int fn(int num, int n[num]);

이처럼, num의 값을 앞에서 설정하고, 그 값을 이용하여 뒤에서 n이라는 배열의 크기를 설정하는 것입니다. 이것을 함수 프로토타입 범위라고 할 수 있습니다.

 

더욱, 확실한 설명을 위해서 아래 예제를 만들어 보았으니 참고해주세요.

  1 #include <stdio.h>
  2 
  3 void fn(int num, int test[num])
  4 {   
  5     for (int i = 0; i < num; i++) {
  6         test[i] = i;
  7         printf("%d", test[i]); // 01234
  8     }
  9 }
 10 
 11 int main(void)
 12 {   
 13     int num = 5;
 14     int test[num];
 15     fn(num, test);
 16     return 0;
 17 }

3번 줄을 보면, 앞의 값을 이용하여 뒤에서 test라는 배열의 크기를 설정해줍니다. 함수 프로토타입 범위는 이러한 경우를 말합니다.

 

아무래도 용어를 번역해서 포스팅하려니, 꽤나 힘든 부분이 많네요. 영어 그대로를 보고자 하시는 분은 아래 링크를 참고해주세요.

https://en.cppreference.com/w/c/language/scope

 

이상으로 포스팅을 마치겠습니다. 제 글이 도움이 되셨다면, 공감 버튼을 한 번씩 눌러주시면 감사하겠습니다. :)

댓글