15편에서 전처리기로 코드를 준비했다면, 이제는 "변수가 언제 만들어지고 언제 사라지는가"를 해부해야 합니다. C에서는 같은 이름의 변수라도 저장 기간(storage duration)과 스코프(scope)에 따라 전혀 다른 수명을 가집니다. 여기에 링키지(linkage)까지 더해지면 "언제까지 살아 있는가", "어디서 보이는가", "다른 파일에서도 같은 이름으로 연결되는가"를 따로 생각해야 합니다. 이 흐름을 이해하면 전역 변수 남용을 피하고, 모듈 간 데이터를 주고받을 때도 혼란이 줄어듭니다.
이번 글에서 새로 나오는 용어
- 저장 기간 (Storage Duration): 메모리에 값이 얼마나 오래 남아 있는지
- 자동 저장 기간 (Automatic Storage): 블록을 벗어나면 사라지는 지역 변수의 수명
- 정적 저장 기간 (Static Storage): 프로그램 시작부터 종료까지 유지되는 변수 수명
- 스코프 (Scope): 변수를 참조할 수 있는 코드 영역
핵심 개념
학습 메모
- 소요 시간: 60분
- 준비물: 함수, 전처리, 헤더/소스 분리 경험
- 학습 목표:
static,extern으로 저장 기간을 제어하고, 블록/파일 스코프를 구분하기
저장 기간과 스코프는 함께 다뤄야 합니다.
- 저장 기간은 메모리가 살아 있는 시간을 말합니다. 주요 유형은 자동, 정적, 동적입니다.
- 스코프는 코드에서 그 이름을 부를 수 있는 범위입니다.
- 링키지는 같은 이름이 다른 파일에서도 같은 대상을 가리키는지를 뜻합니다.
이 둘이 항상 같은 것은 아닙니다. 예를 들어 함수 안의 static 변수는 정적 저장 기간(프로그램 전체 유지)을 가지지만, 스코프는 그 함수 내부에만 제한됩니다. 이번 글에서는 아래 내용을 다룹니다.
- 자동 변수와 블록 스코프
- 전역 변수와 파일 스코프
static키워드를 이용한 내부 연결(Internal Linkage)extern선언으로 다른 파일의 전역 변수 사용하기- 정적 지역 변수 예제와 활용 팁
코드로 따라하기
자동 저장 기간과 블록 스코프
#include <stdio.h>
int main(void) {
int count = 0; // 자동 저장 기간
if (1) {
int count = 10; // 새 블록 scope
printf("블록 내부 count = %d\n", count);
}
printf("바깥 count = %d\n", count);
return 0;
}
if 블록 안에서 같은 이름의 변수를 다시 선언하면, 블록을 벗어날 때 사라집니다. 자동 저장 기간 변수는 함수가 끝나면 메모리가 해제되므로, 주소를 반환하면 안 됩니다.
전역 변수와 파일 스코프
#include <stdio.h>
int g_total = 0; // 파일 스코프 + 정적 저장 기간
void add_score(int value) {
g_total += value;
}
int main(void) {
add_score(10);
add_score(5);
printf("총점 = %d\n", g_total);
return 0;
}
파일 스코프 변수는 선언 위치 아래에서 어디서든 참조할 수 있고, 프로그램 전체 실행 동안 값을 유지합니다. 하지만 전역 변수는 남용하면 의존성이 늘어나므로, 필요한 경우에만 사용하고 나머지는 함수 인자나 구조체로 전달합니다.
static으로 내부 연결 만들기
// counter.c
#include <stdio.h>
static int internal_total = 0; // 다른 파일에서 보이지 않음
void increase(void) {
internal_total++;
printf("internal_total = %d\n", internal_total);
}
static을 파일 스코프 변수나 함수 앞에 붙이면 "internal linkage"가 되어 다른 번역 단위에서 심볼을 볼 수 없습니다. 헤더에 노출하고 싶지 않은 내부 상태를 감출 때 유용합니다.
즉, static은 문맥에 따라 역할이 조금 다릅니다.
- 파일 스코프의
static: 다른 파일에서 보이지 않게 숨김 - 함수 내부의
static: 함수가 끝나도 값이 유지됨
정적 지역 변수
#include <stdio.h>
void greet(void) {
static int called = 0; // 정적 저장 기간 + 블록 스코프
called++;
printf("greet가 %d번째 호출되었습니다.\n", called);
}
int main(void) {
greet();
greet();
greet();
return 0;
}
static 지역 변수는 함수가 끝나도 값이 유지됩니다. 이 방법은 함수 호출 횟수, 캐시, 지연 초기화(lazy initialization) 등에 활용됩니다. 단, 멀티스레드 환경에서는 동시에 접근하지 않도록 주의해야 합니다.
extern으로 다른 파일 변수 접근하기
// config.c
int g_mode = 1;
// main.c
#include <stdio.h>
extern int g_mode; // 다른 파일에서 정의된 전역 변수 선언
int main(void) {
if (g_mode == 1) {
printf("표준 모드\n");
}
return 0;
}
extern 선언은 "이 이름의 변수가 어딘가에 정의되어 있으니, 여기서는 그 정의를 참조하겠다"는 의미입니다. 즉, extern 자체가 메모리를 새로 만드는 것이 아니라 이미 다른 곳에 있는 정의를 연결해 쓰겠다는 선언에 가깝습니다. 헤더 파일에 extern 선언만 두고, 실제 정의는 단 하나의 .c 파일에 두는 것이 관례입니다.
저장 기간 정리 표
| 저장 기간 | 키워드/예시 | 수명 | 스코프 예시 |
|---|---|---|---|
| 자동 | 함수 내부 변수 | 블록 종료 시 | 블록 내부 |
| 정적 | 전역 변수, static 지역 |
프로그램 전체 | 파일 스코프 또는 블록 스코프 |
| 동적 | malloc/free |
free 호출 전까지 |
포인터가 닿는 곳 어디든 |
이 표를 항상 떠올리면, 변수 한 개를 선언할 때도 "누가 언제까지 들고 있을까"를 함께 생각하게 됩니다.
왜 중요한가
- 저장 기간을 이해해야 메모리 오류와 전역 상태 문제를 예방할 수 있습니다.
static,extern키워드는 모듈 경계를 설정하고 캡슐화를 돕습니다.- 이후 함수 포인터, 빌드 자동화 주제로 넘어가도 변수 수명이 머릿속에 잡혀 있어야 버그를 빠르게 추적할 수 있습니다.
CodeSandbox로 이어서 실습하기
아래 샌드박스는 CodeSandbox의 Universal starter입니다. C는 터미널에서 직접 컴파일하고 다시 실행하는 흐름이 중요하니, 이번 글의 코드를 파일로 만들고 빌드-실행 사이클을 다시 따라가 보세요.
💬 댓글
이 글에 대한 의견을 남겨주세요