Language/C

C_Char13-1. 변수 사용 영역

지역변수

우리가 이때까지 앞에서부터 사용했던 변수는 대부분 지여 변수이다.
자료형 변수명; 으로 선언했던것도 사실 auto라는 예약어생략된 문자이었다.
auto예약어는 생략이 가능하며, 이 경우 함수 안에 선언된 변수는 자동으로 지역변수가 된다.

1. 지역 변수 특징

특징 1) 지역 변수는 사용범위가 블록 내부로 제한되므로, 다른 함수에서는 사용할 수 없다.

  • 두 함수에서 같은 이름의 지역 변수를 사용한 경우
#include <stdio.h>

void assign(void);

int main(void)
{
    auto int a = 0;

    assign();
    printf("main 함수 a : %d\n", a);

    return 0;
}

void assign(void)
{
    int a;

    a = 10;
    printf("assign 함수 a : %d\n", a);
}

/* 
[ 출력 ]
assign 함수 a : 10
main 함수 a : 0
*/

이처럼 다른 함수에서 같은 이름의 변수를 선언해도 이름이 서로 충돌하지 않는다.

특징 2) 지역 변수는 이름이 같아도 선언된 함수가 다르다면, 각각 독립된 저장 공간을 갖는다.

두 함수에 각각 선언된 변수 a는 이름만 같을 뿐 메모리에 별도의 저장공간을 갖는다.

 

지역 변수 사용의 장점

1 . 메모리를 효율적으로 사용한다.

필요시에 사용하고, 필요 없을 시엔 반환되어 할당된 저장 공간을 자동으로 회수한다. (재활용)

 

2 . 디버깅에 유리하다.

지역 변수는 값에 문제가 있을 때 main 함수에서만 원인을 찾으면 되서 수정하기 쉽다.
단, 지역 변수는 자동 초기화X, 쓰레기값에 주의해야한다.
또한, 사용 범위가 하나의 함수로 제한되기 떄문에 공유하기가 쉽지 않다.
그러나, 이러한 불편함 보다 장점이 더 크기 때문에 최우선으로 사용해야한다.


2. 블록 안 지역 변수 사용

지역 변수는 보통 함수 안에서 선언한 후 함수 끝까지 사용하지만 선언 위치에 따라 사용범위가 달리질 수 있다.

  • 블록 안에 지역 변수를 사용하여 두 변수를 교환하는 프로그램
#include <stdio.h> 

int main(void)  
{  
int a = 10, b = 20;

printf("교환 전 a와 b의 값 : %d, %d\\n", a, b);  
{  
int temp;


temp = a;
a = b;
b = temp;


}  
printf("교환 후 a와 b의 값 : %d, %d\\n", a, b);

return 0;
}

/*
[ 출력 ]
교환 전 a와 b의 값 : 10, 20
교환 후 a와 b의 값 : 20, 10
*/

규칙 2) 사용 가능한 변수가 둘 이상이면, 가장 가까운 블록에 선언된 변수를 사용한다.

특정 블록 안에 변수를 선언하면, 필요한 경우 잠깐 사용하고, 메모리를 재활용하는 효과를 볼 수 있다.
지역 변수가 사용한 메모리는 블록이 끝나면 동시에 반환된다.

 


3. 전역변수

전역 변수 = 함수 밖에 변수를 선언

특정 함수의 블록에 포함되지 않기 때문에 사용 범위가 함수나 블록으로 제한되지 않는다.

그렇기에 전역 변수의 사용 범위는 프로그램 전체이므로, 어떤 함수라도 안에서 직접 사용할 수 있다.

  • 전역 변수의 사용
#include <stdio.h>

void assign10(void);  
void assign20(void);

int a;

int main(void)  
{  
printf("함수 호출 전 a 값 : %d\\n", a);

assign10();  
assign20();

printf("함수 호출 후 a 값 : %d\\n", a);

return 0;
}

void assign10(void)  
{  
a = 10;  
}

void assign20(void)  
{  
int a;


a = 20;

}

/* 
[ 출력 ]
>> 함수 호출 전 a값 : 0
   함수 호출 후 a값 : 10
*/

전역 변수는 프로그램 시작과 동시에 할당되어, 종료 시까지 존재한다. 또한,

지역 변수는 자동으로 초기화가 되지 않으나, 전역 변수는 0으로 자동 초기화가 된다.

 

전역 변수와 같은 이름의 지역 변수

위 예시처럼 두개의 함수에서 각각 a를 다른 수로 대입하게 되면 첫번째 assign10함수에서는 a로 변경되지만, 두번째 함수 assign20에서는 a 값이 바뀌지 않는다.

이유는, 전역 변수 a와 같은 이름의 지역 변수가 존재하기 때문이다.

이런 경우에는 전역 변수와 지역 변수의 이름이 같으면 지역 변수를 먼저 사용한다.

 

전역 변수의 문제점
모든 함수에서 자유롭게 접근할 수 있으므로, 같은 변수를 여러 함수에 쉽게 공유 할 수 있는 장점이 있으나, 이런 특징은 장점보다 부작용이 더 크므로 사용을 꺼리는 이유가 된다.

 

문제 1) 전역 변수의 이름을 바꾸면 그 변수를 사용하는 모든 함수를 찾아 수정해야한다.

문제 2) 전역 변수의 값이 잘못된 경우 접근 가능한 모든 함수를 의심해야한다.

문제 3) 코드 블록 내에 같은 이름의 지역 변수를 선언하면, 그 영역에서는 전역 변수를 사용할 수 없다.

 

결론, 사용 범위가 명확하고 통제가능한 지역 변수를 우선적으로 사용하고,

전역 변수는 많은 함수에서 수시로 데이터를 공유하는 경우 제한적으로 사용하거나, 사용하지 않는게 좋다.

 


4. 정적 지역 변수

지역 변수를 사용할 때 static 예약어를 사용하면 정적 지역 변수라고 한다.

정적 지역 변수는 코드 블록 안에 선언하므로, 일반 지역 변수와 같이 사용 범위가 블록 내부로 제한된다.

단, 저장 공간이 메모리에 존재하는 기간이 다르다.

 

또한, 일반 변수와 다르게 선언된 함수가 반환되더라도, 그 저장 공간을 계속 유지한다.

따라서, 하나의 함수가 여러 번 호출 되는 경우 같은 변수를 공유 하는것이 가능하다.

 

  • auto 지역 변수와 static 지역 변수의 비교
#include <stdio.h>

void auto\_func(void);  
void static\_func(void);

int main(void)  
{  
	int i;

	printf("일반 지역 변수(auto)를 사용한 함수...\n");
    for (i = 0; i < 3; i++)
	{
	auto_func();
	}

	printf("정적 지역 변수(static)를 사용한 함수...\n");
	for (i = 0; i < 3; i++) {
	static_func();
	}

	return 0;
    }


void auto_func(void)  
{  
	auto int a = 0;
	a++;
	printf("%d\n", a);
}


void static_func(void)  
{  
	static int a;
	a++;
	printf("%d\n", a);

}

/*
[ 출력 ]
5005000
*/

지역 변수는 함수가 호출할 때 메모리에 할당되고, 반환할때 메모리에서 제거된다.

그렇기에 변수 a는 auto_func함수가 호출될 때마다 메모리에 새롭게 할당되고 그때마다 0으로 초기화 된다.

 

반면, 지역 변수에 static 예약어를 사용하면, 저장 공간이 할당되어 유지하는 시점이 함수 호출과 무관하다.  static을 사용한 정적 지역 변수는 프로그램이 실행될 때 메모리에 할당되며, 프로그램이 끝날 때까지 존재한다.

또한 정적 지역 변수는 초기화되지 않으면 0으로 자동 초기화된다. 

 

※ 만약, static 예약어가 함수 밖에 있다면?

저장 공간의 할당과 회수는 함수의 호출 및 반환과 관계가 없다.

 

결론,  정적 지역 변수는 선언된 블록 안에서만 사용하는 전역 변수와 같다. 

 


5. 레지스터 변수

블록 or 함수 내에 변수를 선언할 때 register예약어를 사용한다.

 

그럼, 지역 변수와 레지스터 변수의 차이는 무엇일까?

레지스터 변수의 가장 큰 특징은 저장 공간이 할당되는 위치 이다.

이름과 같이 레지스터 변수는 CPU안에 있는 저장 공간인 레지스터를 사용한다.

 

레지스터는 CPU 안에 이어 데이터 처리 속도가 가장 빠른 저장 공간이다.

따라서, 반복문에 쓰이는 변수와 같이 사용 횟수가 많은 경우 레지스터에 할당하면 실행 시간을 줄일 수 있다.

 

  • 레지스터 변수를 반복문에 사용
#include <stdio.h> 

int main(void)
{
	register int i;
	auto int sum = 0;

	for (i = 1; i <= 10000; i++);
	{
		sum += i;
	}
	printf("%d\n", sum);

	return 0;
}

이렇게 자주 사용하는 변수를 레지스터 변수로 선언하면, 변수의 저장 공간이 램 메모리에 있을 때보다 빨라 프로그램 실행 시간을 주일 수 있다.

 

근데 왜 레지스터를 이용하면 빨라지는가?

메인 메모리에 있는 일반 변수의 값은 레지스터로 옮긴 후 연산 장치에 사용되기 때문에

레지스터에 값을 저장하면 메인 메모리에 접근하는 시간을 줄일 수 있다.

 

레지스터 변수 사용 시 주의점

1) 전역 변수는 레지스터 변수로 선언할 수 없다.

레지스터 변수는 CPU의 자원을 잠깐 빌리는 것이므로 프로그램을 실행하는 동안 저장 공간을 계속 확보해야 하는 전역 변수는 레지스터에 할당할 수 없다.

 

2) 레지스터 변수는 주소를 수할 수 없다.

저장 공간이 메모리에 있는 것이 아니기 때문에 주소 연산자를 써서 주소를 구할 수 없다.

 

3) 레지스터의 사용 여부는 컴파일러가 결정한다.

레지스터 변수를 선언했다고 다 생성되는게 아니며, 생성 위치는 컴파일러가 레지스터와 메모리 중 어디 할당하는 것이 더 이득인지 판단하여 상황에 따라 정한다.

 


 

[ 중간 정리 ]

1. 지역 변수의 사용 범위는 블록으로 제한된다.

 

2. 지역 변수와 전역 변수의 사용 범위가 겹쳐지면 지역 변수를 먼저 사용한다.

 

3. 지역 변수에 static 을 사용해서 정적 지역 변수로 만들면 프로그램의 시작부터 종료까지 저장 공간이 유지된다.

 

4. 레지스터 변수는 컴파일러가 레지스터에 생성할지 말지 결정한다. (사용자 결정 X)

 

[ 표로 정리하기 ]

- 여러 가지 변수의 특징 

 

  

'Language > C' 카테고리의 다른 글

C_Char01. 프로그램과 C언어  (0) 2021.06.05
C_Char13-2. 함수의 데이터 공유 방법  (0) 2021.05.10
C_Char09.포인터(이해)  (0) 2021.05.09
C_Char09.포인터(개념)  (0) 2021.05.09