목차
- 정적 변수 static
- register 변수
- volatile 변수
1. 정적 변수 static
일반적으로 모든 블록 내부 변수들에는 자동적으로 auto가 붙습니다. 자동 변수라 하죠.
자동이라는 이름 답게, 변수 선언 위치에서 자동으로 만들어지고 블럭을 벗어나면 자동으로 소멸합니다.
하지만 우리는 이 변수를 좀 오래 살리고 싶어요.
저장 유형 지정자 static를 사용하면 이 변수를 오래 살릴 수 있습니다.
이 변수는 함수 내부에서 선언되지만 프로그램이 종료될 때까지 살아있으며,
정적 변수처럼 선언하자마자 자동으로 초기화됩니다.
지역 변수처럼 접근되지만 전역변수처럼 수명이 길어요.
일반적으로 다음과 같은 상황에서 사용됩니다.
- 함수 호출 횟수를 세거나
- 한번만 초기화하고 값을 유지하거나
- 함수 내부에 특수한 정부보를 저장하거나 말이죠.
예제로 알아봅시다.
#include <stdio.h>
void sub(void);
int main(void)
{
sub();
sub();
sub();
return 0;
}
void sub()
{
static int score; //정적 변수 score 선언(자동으로 0으로 초기화됨)
auto int count = 0; //지역 변수 count 선언(auto 지정자는 생략 가능)
printf("static : %d\t", score);
printf("auto : %d\n", count);
score++; //1씩 더함
count++; //1씩 더함
}
보시면 static는 선언과 동시에 자동으로 초기화되지만, auto는 초기화되지 않아서 직접 초기화하는 것을 알 수 있습니다.
출력은 다음과 같아요.
static : 0 auto : 0
static : 1 auto : 0
static : 2 auto : 0
static는 정적 변수처럼 사용되기 때문에 프로그램이 끝날 때까지도 살아있습니다.
반대로 auto는 함수가 종료되면 사라지므로, sub가 종료될 때 지워지고 호출될 때 다시 생겨나는 것을 볼 수 있네요.
2. register 변수
변수의 자료형 앞에 register을 붙이면 CPU 내부의 레지스터(Register)에 변수가 저장됩니다.
딱히 쓸 일은 없어요. 빠르다는 장점은 있습니다만, 안 그래도 작은 CPU의 더 작은 레지스터 공간을 사용하므로...
그리고 요즘 RAM 등의 발전 덕분에 이정도까지 빠르게 작업할 필요는 없습니다.
다만 사용한다면 다음과 같은 주의점을 기억하세요.
- 속도는 확실히 올라갑니다, CPU에 직결연결되니까요.
- 저장공간이 모자랍니다. CPU는 작고, 레지스터는 그보다 더 작죠.
- 전역 변수는 레지스터 변수로 선언할 수 없습니다, 일정 영역을 지속적으로 점유하는 전역변수는 선언할 수 없어요.
- 주소를 못 얻습니다. 그러니까, &을 사용할 수 없어요.
- 요즘 컴파일러는 성능이 좋습니다. 이미 필요한 변수들을 알아서 레지스터에 배치해요.
- 문제는 그 덕분에 컴파일러는 register을 붙여도 이걸 레지스터에 저장안합니다. 필요할 때만 가져다 사용해요.
그러면 왜 있냐고요?
그냥 과거의 잔재입니다. C는 50년 정도 사용된 구닥다리 언어임을 기억하세요.
과거에는 컴파일러 성능이 떨어져서 개발자가 직접 레지스터를 할당했지만..
지금은 최적화와 컴퓨터 성능이 향상되어서 C가 컴파일러의 발전 속도를 못 따라갔달까..
그래도 한번 살펴보기나 하죠.
레지스터를 사용한 변수의 속도를 확인하기에는 꽤나 복잡한 감이 있습니다만,
그럼에도 한번 확인해보면 속도는..
Normal : 0.063 seconds
Register : 0.061 seconds
어라 별로 안 빠른데
컴파일러 성능이 좋나봅니다.
#include <stdio.h>
int main(void)
{
register char *ps = { "Welcome to C world!" };
printf("%s", ps);
return 0;
}
음, register을 쓰면 배열 저장 못한다매요?
음, 실제로는 배열을 저장하는게 아닙니다.
그냥 저 문자열을 어딘가에 저장하고, register int에는 그냥 ps의 주소값만 저장합니다.
이런게 있긴 하지만, 그냥 쓰지 마세요.
3. volatile 변수
volatile 지정자는 하드웨어가 수시로 변수의 값을 변경하는 경우에 사용됩니다.
변수가 volatile로 선언되면 컴파일러는 항상 이 변수의 최신 값을 불러오도록 합니다.
예를 들어, 어떤 변수가 volatile로 선언되고 0이 저장되었다면, 컴파일러는 이 변수의 값이 변하든 변하지 않는 바로 업데이트를 해 옵니다. 하드웨어가 직접 이 변수를 수정한 것을 바로 알 수 있죠.
이 volatile 변수는 다음과 같은 상황에서 사용됩니다.
- 하드웨어 레지스터 접근
- 인터럽트 서비스 루틴
- 멀티스레딩 환경
이 volatile 변수는 외부 요인으로 인한 값 변경이 예상되는 상황에서 사용됩니다.
항상 변수의 최신 상황을 보여줘요.
그리고 컴파일러는 이 변수를 건드리지 않습니다.
예제로 알아봅시다.
#include <stdio.h>
volatile int io_port;
int main(void)
{
while (io_port != 255)
printf("%d", io_port);
return 0;
}
출력은 다음과 같습니다.
00000000000000000000000000000000000000....
만약 이 변수가 하드웨어에 의해 변한다면 다른 값이 나오겠죠?
결론은 다음과 같네요!
종류 | 지역 변수 | 전역 변수 | ||||
예약어 | auto(생략 가능) | register | static | static | 없음 | exturn |
선언 위치 | 블럭 내부 | 함수 외부 | ||||
사용 범위 | 선언 ~ 선언한 블럭 끝 | 하나의 파일 내부 | 프로그램 전체 | |||
수명 | 선언 ~ 선언한 블럭 끝 | 프로그램 시작 ~ 종료 | ||||
자동 초기화 | 없음 | 0으로 자동 초기화 | ||||
메모리 위치 | 메모리 스텍 | 레지스터 | 데이터 영역 |
volatile의 경우, 실제로는 종류(class)가 아닌 속성(attribute)이기 때문에 예약어에 따라 메모리 위치가 달라집니다.
전역 변수에 대한 설명이 이전 장에서 좀 모자란데, extern 선언과 static 전역 변수는 분할 컴파일과 관련된 내용이므로 따로 다룹니다.
'프로그래밍 > C' 카테고리의 다른 글
배열 - 예제 모음 (0) | 2025.06.11 |
---|---|
함수의 데이터 공유 (0) | 2025.06.11 |
변수 사용 영역 (0) | 2025.06.10 |
버퍼를 사용하는 입력 함수 (0) | 2025.06.09 |
문자 (4) | 2025.06.07 |