
C언어에서 C언어 static 변수 개념은 처음 배울 때 이상하게 손에 잘 안 잡히는 키워드입니다. 같은 static인데 어떤 때는 값이 계속 남는다고 하고, 어떤 때는 이 파일 안에서만 보인다고 말하기 때문입니다.
이 글은 static을 한 단어로 외우지 않고 수명(storage duration)과 가시성(linkage)을 분리해서 설명합니다. 함수 안 static 변수와 파일 범위 static 변수 차이를 잡으면 C의 변수 모델이 훨씬 덜 헷갈립니다.

먼저 한 줄 결론부터
static은 상황에 따라 두 가지를 도와줍니다. 블록 안에서는 값을 함수 호출 뒤에도 유지하게 만들고, 파일 범위에서는 그 이름을 다른 파일에서 못 보게 막습니다. 즉 static을 보면 먼저 “지금 이 선언이 함수 안인가, 파일 범위인가”부터 물어보는 것이 가장 안전합니다.
- 함수 안 static → 지역 이름을 가진 오래 사는 상태
- 파일 범위 static → 오래 살지만 이 파일 안에서만 보이는 이름
C언어 static은 “오래 산다”와 “밖에서 안 보인다”를 같은 뜻으로 섞지 않을 때 훨씬 쉬워집니다.
C언어 static 변수는 왜 자꾸 헷갈릴까
입문자는 보통 변수의 사는 기간과 어디서 보이는가를 한 덩어리로 기억합니다. 하지만 C에서는 storage duration과 linkage가 다른 성질입니다. 일반 지역 변수는 함수가 끝나면 사라지고, 전역 변수는 프로그램이 끝날 때까지 삽니다. 그런데 지역 static 변수는 이름은 지역 변수처럼 함수 안에 묶여 있으면서 수명은 전역 객체처럼 오래 갑니다. 바로 이 중간 성격 때문에 많이 꼬입니다.

함수 안의 static 변수부터 보기
가장 먼저 이해하기 쉬운 쪽은 함수 안의 static 변수입니다. 이 경우 핵심은 함수 호출이 끝난 뒤에도 값이 남는다는 점입니다.
#include <stdio.h>
void visit_count(void)
{
static int count = 0;
count++;
printf("count = %d\n", count);
}
int main(void)
{
visit_count();
visit_count();
visit_count();
return 0;
}이 코드를 실행하면 count는 1, 2, 3으로 증가합니다. 보통 지역 변수였다면 함수가 끝날 때마다 사라져서 매번 1이 나와야 하지만, static을 붙였기 때문에 이전 호출의 값이 유지됩니다. cppreference는 static storage duration 객체의 lifetime이 프로그램 전체 실행 동안 이어지고 stored value is initialized only once라고 설명합니다.
일반 지역 변수와 바로 비교해 보기
#include <stdio.h>
void compare_counter(void)
{
int normal = 0;
static int persistent = 0;
normal++;
persistent++;
printf("normal = %d, persistent = %d\n", normal, persistent);
}
int main(void)
{
compare_counter();
compare_counter();
compare_counter();
return 0;
}출력은 보통 normal은 항상 1이고 persistent는 1, 2, 3으로 늘어납니다. 즉 normal은 호출마다 다시 시작하고, persistent는 이전 값을 기억합니다. 포인터 기초 글에서 메모리 감각이 아직 흐리다면 함께 보면 이해가 더 쉬워집니다.
scope는 왜 여전히 지역일까
void foo(void)
{
static int cache = 42;
}
int main(void)
{
/* printf("%d\n", cache); // 오류 */
return 0;
}cache는 프로그램이 끝날 때까지 살아 있어도 이름은 foo 블록 안에서만 유효합니다. 즉 지역 static 변수는 수명은 길지만 scope는 지역입니다. 이 점이 가장 중요합니다.
지역 static 변수는 언제 유용할까
- 함수 호출 횟수를 세고 싶을 때
- 한 번 계산한 값을 다음 호출에서 재사용하고 싶을 때
- 외부에 전역 이름을 노출하지 않고 함수 내부 상태를 유지하고 싶을 때
int next_id(void)
{
static int current = 1000;
return current++;
}이 함수는 호출할 때마다 1000, 1001, 1002처럼 다음 값을 돌려줄 수 있습니다. 전역 변수를 바깥으로 공개하지 않고도 함수 내부 상태를 유지할 수 있다는 점이 장점입니다. 다만 함수가 숨은 상태를 품게 되므로 테스트와 재진입성 관점에서는 조심해야 합니다.

파일 범위 static 변수는 무엇이 다를까
이번에는 함수 밖, 즉 파일 범위의 static을 보겠습니다. 여기서는 값 유지 자체보다 이 이름을 다른 파일에서 못 보게 막는 역할이 더 중요합니다.
/* a.c */
int visible_global = 10;
static int hidden_global = 20;
/* b.c */
extern int visible_global;
/* extern int hidden_global; // 사용할 수 없음 */visible_global은 다른 파일에서 extern으로 참조할 수 있지만, hidden_global은 static이므로 이 파일 바깥으로 이름이 공개되지 않습니다. cppreference는 block scope가 아닌 static이 internal linkage를 가진다고 설명합니다. 쉽게 말해 프로그램 내내 살지만 이 파일 안에서만 쓸 수 있는 이름입니다.
일반 전역 변수와 어떻게 다를까
- int g; → 다른 파일에서도 참조 가능한 전역 이름이 될 수 있음
- static int g; → 이 파일 안에서만 쓰는 내부 전용 전역 이름
둘 다 오래 산다는 점만 보면 비슷하지만, 실제로 중요한 차이는 링크 범위입니다. 그래서 파일 범위 static은 메모리보다도 “이 이름을 외부에 공개하지 않는다”는 의도로 이해하는 편이 더 정확합니다.
표로 한 번에 정리
- int x; (함수 안) → 블록 안에서만 보이고, 함수 호출 동안만 산다
- static int x; (함수 안) → 블록 안에서만 보이지만 프로그램 전체 동안 산다
- int x; (파일 범위) → 프로그램 전체 동안 살고 다른 파일에서 참조 가능하다
- static int x; (파일 범위) → 프로그램 전체 동안 살지만 이 파일 안에서만 이름이 보인다
이 기준표를 머릿속에 두면 static 하나로 모든 걸 설명하려 하지 않게 됩니다. 선언 위치와 수명·가시성을 함께 봐야 한다는 점이 핵심입니다.
많이 틀리는 오해 3가지
1. static이면 무조건 전역처럼 쓸 수 있다
아닙니다. 지역 static 변수는 오래 살아도 이름은 그 함수 블록 안에서만 유효합니다. 오래 산다는 것과 어디서나 보인다는 것은 다른 문제입니다.
2. static이면 함수 호출마다 다시 초기화된다
아닙니다. static storage duration 객체는 초기화가 한 번만 일어나는 쪽으로 이해하는 것이 맞습니다. 그래서 함수 안 static 변수는 이전 호출 값을 계속 이어받습니다.
3. 파일 범위 static은 그냥 오래 사는 전역 변수다
절반만 맞습니다. 중요한 포인트는 “오래 산다”보다도 “다른 파일에 이름을 공개하지 않는다”는 점입니다. internal linkage 개념이 빠지면 file scope static의 본질을 놓치게 됩니다.
함수에도 file scope static을 붙일 수 있다
static void helper(void)
{
/* 이 파일 안에서만 쓰는 보조 함수 */
}변수뿐 아니라 함수에도 static을 붙일 수 있습니다. 이 경우도 핵심은 비슷합니다. helper라는 이름을 다른 파일로 공개하지 않겠다는 뜻입니다. comp.lang.c FAQ는 static 선언 규칙이 미묘할 수 있으니 정의와 선언에서 static을 일관되게 쓰는 편이 안전하다고 조언합니다.
언제 static을 쓰고, 언제 조심해야 할까
특히 local static은 편리하지만 함수가 입력만 보고 동작하지 않게 만들 수 있습니다. 즉 호출 횟수나 과거 상태가 결과에 영향을 주기 시작하면 테스트와 재사용이 어려워질 수 있습니다.
그래서 static은 무조건 피해야 하는 키워드가 아니라, 숨은 상태를 감수할 가치가 있는지 먼저 따져야 하는 도구라고 보는 편이 더 실무적입니다.
- 함수 내부 상태를 다음 호출까지 유지하고 싶을 때
- 전역 이름을 외부 파일에 노출하고 싶지 않을 때
- 파일 내부 전용 헬퍼 함수와 변수를 만들고 싶을 때
- 반대로 재진입성, 테스트 초기화, 병렬 실행이 중요할 때는 남용을 조심할 것
지역 static은 편리하지만 함수가 숨은 상태를 가지게 만듭니다. 그래서 디버깅과 테스트가 어려워질 수 있습니다. 전역 공개 범위를 줄이는 file scope static은 좋은 습관이 될 수 있지만, 상태 유지용 static local은 목적이 분명할 때만 쓰는 편이 좋습니다.
선언 읽는 감각을 더 잡고 싶다면 C 언어 struct와 typedef 차이, 포인터 한정자까지 연결해서 보고 싶다면 C 언어 const 포인터 정리도 이어서 읽기 좋습니다.
정리
- static은 항상 같은 뜻으로 읽으면 안 된다
- 함수 안에서는 호출 사이 값 유지가 핵심이다
- 파일 범위에서는 다른 파일에 이름을 숨기는 것이 핵심이다
- 지역 static은 scope는 지역이고 lifetime은 길다
- 파일 범위 static은 lifetime은 길고 linkage는 internal이다
C언어 static은 먼저 선언 위치를 보고, 그다음 수명과 가시성을 따로 판단하면 풀립니다.
더 자세한 기준은 cppreference의 static storage duration 문서, storage-class specifiers 문서, 그리고 comp.lang.c FAQ의 static 관련 설명을 참고했습니다.