실무자가 반드시 알아야 할 Static, Extern, Global 변수의 차이
Static, Extern, Global 변수
들어가며
AUTOSAR 프로젝트에서 이런 상황이 자주 발생해요:
신입 개발자:
파일1.c:
static int counter = 0; // ?
파일2.c:
extern int counter; // ?
main.c:
int global_counter = 0; // ?
"이게 다 뭐예요? 다 같은 거 아니에요?"
선배:
"음... static은 내부용, extern은 외부용...?"
신입:
"그래서 counter는 어디에 있어요???"
결과: 링킹 오류 + 디버깅 2시간 😭
이 혼동을 끝내자!
이 글에서는 static, extern, global의 정확한 정의, 스코프, 메모리,
그리고 언제 뭘 써야 하는지 명확하게 설명해볼게요
-> 3분 요약
바쁜 당신을 위한 핵심:
- static = "이 파일 안에서만 사용" (내부/private)
- global = "모든 파일에서 사용 가능" (외부/public)
- extern = "다른 파일에 정의된 변수 사용" (참조/링크)
static은 숨김, global은 공개, extern은 빌려쓰기!

1. Global 변수 (전역 변수)란?
정의
Global = 모든 파일에서 접근 가능한 변수
특징:
1. 어디서나 접근 가능 (public)
2. 파일을 넘어 프로그램 전체에서 사용
3. 메모리에 프로그램 시작부터 끝까지 존재
4. 초기화: 없으면 0으로 자동 초기화
Global 변수 예시
// engineControl.c (파일1)
int globalRPM = 0; // Global 변수
// 모든 파일에서 접근 가능!
void EngineControl_Main() {
globalRPM = 3000;
printf("RPM = %d\n", globalRPM);
}
// transmissionControl.c (파일2)
extern int globalRPM; // 외부에서 선언된 globalRPM 사용
void TransmissionControl_Main() {
if (globalRPM > 5000) {
// Shift down
}
}
// main.c
int main() {
EngineControl_Main(); // globalRPM = 3000
TransmissionControl_Main(); // globalRPM 읽음
return 0;
}
결과: globalRPM이 모든 파일에서 공유됨!
Global 변수의 메모리
메모리 레이아웃:
┌─────────────────────┐
│ Code (코드) │
├─────────────────────┤
│ Data (초기화된 전역) │
│ └─ globalRPM = 0 │ ← Global 변수
├─────────────────────┤
│ BSS (초기화 안 됨) │
│ └─ globalVar │
├─────────────────────┤
│ Heap (동적 할당) │
├─────────────────────┤
│ Stack (로컬 변수) │
└─────────────────────┘
특징:
- 프로그램 시작 → 메모리 할당
- 프로그램 끝 → 메모리 해제
- 생명주기: 프로그램 전체
Global 변수의 장단점
장점:
1️. 모든 함수에서 접근 가능
└─ 데이터 공유 쉬움
2️. 함수 매개변수로 전달 불필요
└─ 함수 시그니처 간단
3️. 상태 유지 가능
└─ 함수 호출 사이에 값 보존
단점:
1️. 예측 불가능한 수정
└─ 어디서나 바뀔 수 있음
2️. 버그 추적 어려움
└─ globalRPM을 언제 누가 바꿨나?
3️. 테스트 어려움
└─ 상태가 누적됨
4️. 멀티스레드 문제
└─ Race condition 위험

2. Static 변수 (정적 변수)란?
정의
Static = 이 파일 내부에서만 사용 가능한 변수 (파일 스코프)
특징:
1. 파일 안에서만 접근 가능 (private)
2. 다른 파일에서는 접근 불가
3. 같은 파일 내에서는 global처럼 사용
4. 메모리에 프로그램 시작부터 끝까지 존재
5. 초기화: 없으면 0으로 자동 초기화
Static 변수 (파일 레벨)
// engineControl.c (파일1)
static int engineCounter = 0; // Static 변수
// 이 파일 안에서만!
void EngineControl_Init() {
engineCounter = 0;
}
void EngineControl_Main() {
engineCounter++;
printf("Engine run count: %d\n", engineCounter);
}
// transmissionControl.c (파일2)
// extern int engineCounter; // X 오류! 접근 불가!
void TransmissionControl_Main() {
// engineCounter++; // X 이 변수는 다른 파일 것
// 접근 불가!
}
// main.c
int main() {
EngineControl_Init();
EngineControl_Main(); // engineCounter = 1
EngineControl_Main(); // engineCounter = 2
EngineControl_Main(); // engineCounter = 3
return 0;
}
결과: engineCounter는 engineControl.c 안에만 존재!
다른 파일에서는 접근 불가!
Static 변수 (함수 레벨)
// 함수 내의 static 변수
void EngineControl_ProcessData() {
static int processCount = 0; // Static 변수 (함수 내)
processCount++;
printf("Process count: %d\n", processCount);
}
// main.c
int main() {
EngineControl_ProcessData(); // processCount = 1
EngineControl_ProcessData(); // processCount = 2
EngineControl_ProcessData(); // processCount = 3
// processCount를 직접 접근?
// X 불가능! (함수 내 static이므로)
return 0;
}
특징:
1. 함수 호출마다 값이 유지됨 (로컬과 다름)
2. 함수 내에서만 접근 가능
3. 초기화는 프로그램 시작시 딱 한 번
4. 이후 호출마다 이전 값 유지
변수를 함수 내에서만 쓰는 변수라면 함수 내 static으로 범위를 한정시켜주는 것이죠
근데 이방법은 디버그를 할때 변수 파악이 조금 번거로워서 저는 자주 사용하지는 않습니다
Static 변수의 메모리
메모리 레이아웃:
┌─────────────────────┐
│ Code (코드) │
├─────────────────────┤
│ Data (초기화된 전역) │
│ ├─ globalRPM │ ← Global (모든 파일 공유)
│ └─ engineCounter │ ← Static (engineControl.c만)
├─────────────────────┤
│ Stack (로컬 변수) │
│ └─ processCount │ ← Static (ProcessData 함수 내)
└─────────────────────┘
특징:
- Global: 모든 파일에서 접근
- Static (파일): 같은 파일만 접근
- Static (함수): 함수 내만 접근
Static 변수의 용도
1️. 파일 내부 전용 변수
├─ 다른 파일에서 수정 방지
├─ 실수로 인한 변경 방지
└─ 캡슐화 (Encapsulation)
예:
static int errorCount = 0; // 에러 카운터 (내부용)
2️. 함수 내 상태 유지
├─ 함수 호출 사이 값 보존
├─ 호출 횟수 추적
└─ 이전 값 기억
예:
void LogEvent() {
static int eventID = 0;
eventID++; // 매번 증가 (값 유지)
}
3️. Singleton 패턴
├─ 단 하나만 존재하는 객체
├─ 스레드 안전성 고려
└─ 중앙 집중식 관리
예:
ConfigManager* GetConfigManager() {
static ConfigManager manager; // 한 번만 생성
return &manager;
}

3. Extern 변수 (외부 참조)란?
정의
Extern = 다른 파일에 정의된 변수를 현재 파일에서 사용
특징:
1. "다른 곳에 있으니 거기서 가져와" 선언
2. 메모리 할당 안 함 (참조만)
3. 링킹 시 실제 변수와 연결
4. 링킹 오류 가능 (변수 없으면)
Extern의 작동 원리
파일 구조:
┌─────────────────────────┐
│ engineControl.c (정의) │
├─────────────────────────┤
│ int globalRPM = 0; │ ← 메모리에 할당
│ │
│ void SetRPM(int rpm) { │
│ globalRPM = rpm; │
│ } │
└─────────────────────────┘
↑
│ (링킹)
│
┌─────────────────────────┐
│ transmissionControl.c │
│ (사용) │
├─────────────────────────┤
│ extern int globalRPM; │ ← 메모리 할당 안 함
│ │ (다른 파일 참조)
│ void CheckRPM() { │
│ if (globalRPM > ...) │ ← 링킹된 globalRPM 접근
│ } │
└─────────────────────────┘
링킹 과정:
1️. 컴파일: 각 파일을 .o로 변환
└─ engineControl.o: globalRPM 심볼 정의
└─ transmissionControl.o: globalRPM 심볼 참조
2️. 링킹: .o 파일 연결
└─ transmissionControl.o에서 globalRPM 찾음
└─ engineControl.o에서 globalRPM 발견
└─ 주소 연결 (Relocation)
결과: transmissionControl.c에서 globalRPM 사용 가능!
Extern 변수 예시
// engineControl.c
int globalRPM = 0; // 정의 (메모리 할당)
int globalTorque; // 정의 (0으로 초기화)
void SetEngineData(int rpm, int torque) {
globalRPM = rpm;
globalTorque = torque;
}
// transmissionControl.c
extern int globalRPM; // 선언 (참조)
extern int globalTorque; // 선언 (참조)
void AdjustGear() {
if (globalRPM > 5000) {
// 기어 변경
}
}
// diagnosticServer.c
extern int globalRPM; // 선언 (참조)
extern int globalTorque; // 선언 (참조)
void ReportEngineStatus() {
printf("RPM: %d, Torque: %d\n",
globalRPM, globalTorque);
}
// main.c
int main() {
SetEngineData(3000, 200);
AdjustGear(); // globalRPM = 3000 읽음
ReportEngineStatus(); // globalRPM, globalTorque 읽음
return 0;
}
흐름:
SetEngineData()
↓
globalRPM = 3000 (메모리에 저장)
↓
AdjustGear()에서 extern으로 참조
↓
globalRPM 값 읽음!
Extern 링킹 오류
X 오류 1: 정의 없음
transmissionControl.c:
extern int unknownVar; // "있을 거야"라고 기대
engineControl.c:
// unknownVar를 정의하지 않음
링킹 결과:
undefined reference to 'unknownVar'
해결:
1️. engineControl.c에 정의 추가
int unknownVar = 0;
2️. 또는 extern 제거
X 오류 2: 중복 정의
engineControl.c:
int globalCounter = 0; // 정의
transmissionControl.c:
int globalCounter = 0; // 또 정의?
링킹 결과:
multiple definition of 'globalCounter'
해결:
transmissionControl.c에서는:
extern int globalCounter; // 선언만!
X 오류 3: static 변수를 extern으로
engineControl.c:
static int secretCounter = 0; // Static!
transmissionControl.c:
extern int secretCounter; // X 접근 불가!
링킹 결과:
undefined reference to 'secretCounter'
이유: secretCounter는 engineControl.c 내부용
해결:
static을 제거하거나 다른 접근 방법 필요

4. Global, Static, Extern 완벽 비교
| 특징 | Global | Static (파일) | Static (함수) | Extern |
|------|--------|-------------|------------|--------|
| **선언** | int x; | static int x; | static int x; | extern int x; |
| **정의** | 정의 | 정의 | 정의 | 선언만 |
| **메모리** | 할당 | 할당 | 할당 | 할당 안 함 |
| **스코프** | 모든 파일 | 같은 파일 | 같은 함수 | extern 선언한 파일 |
| **접근** | 어디서나 | 파일 내만 | 함수 내만 | extern 필요 |
| **생명주기** | 전체 | 전체 | 전체 | 전체 |
| **초기화** | 자동 0 | 자동 0 | 자동 0 | 없음 |
| **링킹** | 심볼 노출 | 심볼 숨김 | 심볼 없음 | 심볼 찾음 |
우선순위:
스코프 제한: static > extern > global
단순함: global > extern > static
안전성: static > extern > global
5. 실무 시나리오
시나리오 1: 상태 추적 (Static 사용)
// errorHandler.c
static int errorCount = 0; // Static (이 파일 내부용)
static int warningCount = 0;
void LogError() {
errorCount++;
printf("Error #%d\n", errorCount);
}
void LogWarning() {
warningCount++;
printf("Warning #%d\n", warningCount);
}
int GetErrorCount() {
return errorCount; // 내부 상태 외부 공개
}
// main.c
int main() {
LogError(); // errorCount = 1
LogWarning(); // warningCount = 1
LogError(); // errorCount = 2
printf("Total errors: %d\n", GetErrorCount()); // 2
// errorCount++? -> 불가능 (static이므로)
return 0;
}
장점:
1. 실수로 errorCount 직접 수정 불가
2. LogError() 함수를 통해서만 수정
3. 캡슐화 (Encapsulation)
4. 관리 용이
시나리오 2: 데이터 공유 (Global + Extern)
// shared.c (공유 데이터 정의)
int sharedRPM = 0; // Global 정의
int sharedTorque = 0;
void SetSharedData(int rpm, int torque) {
sharedRPM = rpm;
sharedTorque = torque;
}
// engineControl.c
extern int sharedRPM; // 참조
extern int sharedTorque;
void UpdateEngine() {
sharedRPM += 100;
}
// transmissionControl.c
extern int sharedRPM; // 참조
extern int sharedTorque;
void AdjustTransmission() {
if (sharedRPM > 5000) {
// Shift
}
}
// main.c
int main() {
SetSharedData(3000, 200);
UpdateEngine(); // sharedRPM = 3100
AdjustTransmission(); // sharedRPM 읽음
return 0;
}
장점:
1.모든 파일에서 데이터 접근
2.중앙 집중식 관리 (shared.c)
3.간편한 데이터 공유
단점:
어디서나 수정 가능하기 때문에 자칫 실수가능성이 많음
버그 추적 어려움
시나리오 3: AUTOSAR 구조
// rte.c (RTE 생성 코드)
static Rte_Type_EngineData engineData = {0}; // Static
// RTE 내부 상태
Rte_StatusType Rte_Call_SetRPM(uint16 rpm) {
engineData.rpm = rpm;
return E_OK;
}
Rte_StatusType Rte_Call_GetRPM(uint16* rpm) {
*rpm = engineData.rpm;
return E_OK;
}
// EngineControl_Impl.c (개발자 구현)
extern Rte_StatusType Rte_Call_SetRPM(uint16 rpm);
extern Rte_StatusType Rte_Call_GetRPM(uint16* rpm);
void EngineControl_Main() {
Rte_Call_SetRPM(3000); // RTE 함수 호출
}
// DiagnosticServer_Impl.c
extern Rte_StatusType Rte_Call_GetRPM(uint16* rpm);
void ReadEngineRPM() {
uint16 currentRPM;
Rte_Call_GetRPM(¤tRPM); // RTE 함수 호출
}
구조:
Rte 내부: static으로 데이터 보호
개발자: extern으로 RTE 함수 호출
결과: 안전한 인터페이스!
6. 면접 예상 질문
Q1: "Global과 Static의 차이는?"
A: "Global은 모든 파일에서 접근 가능한 전역 변수이고,
Static은 그 파일 내부에서만 접근 가능한 변수입니다.
Static은 '이 파일만 사용' 의도를 나타내는 캡슐화 방법입니다."
Q2: "Extern은 뭐예요?"
A: "다른 파일에 정의된 변수를 현재 파일에서 사용하기 위한 선언입니다.
메모리 할당은 하지 않고, 링킹 시 실제 변수와 연결됩니다."
Q3: "Static 함수 변수는?"
A: "함수 내에 static 변수를 선언하면,
함수 호출마다 값이 유지됩니다.
로컬 변수처럼 함수 내에서만 접근 가능하지만,
프로그램 시작부터 끝까지 메모리에 존재합니다."
Q4: "Global을 피해야 하는 이유는?"
A: "Global은 어디서나 수정 가능해서 버그를 추적하기 어렵고,
멀티스레드 환경에서 Race condition 위험이 있습니다.
Static으로 스코프를 제한하거나,
함수를 통한 접근으로 제어하는 것이 좋습니다."
Q5: "Extern 링킹 오류 해결은?"
A: "undefined reference 오류가 나면:
1️. 변수가 정말 정의되어 있나 확인
2️. 정의 파일도 링킹 대상에 포함되었나 확인
3. Static이나 스코프 문제 아닌지 확인
4️. 정의와 extern 선언이 같은 타입인지 확인"
📌 핵심 정리
| 항목 | Global | Static (파일) | Static (함수) | Extern |
|---|---|---|---|---|
| 정의 | 전역 변수 | 파일 내 변수 | 함수 내 변수 | 외부 참조 |
| 스코프 | 모든 파일 | 같은 파일 | 같은 함수 | 선언한 파일 |
| 메모리 | 할당 O | 할당 O | 할당 O | 할당 안 함 |
| 용도 | 공유 데이터 | 내부 전용 | 상태 추적 | 외부 접근 |
| 장점 | 간편함 | 캡슐화 | 상태 유지 | 명시적 |
| 단점 | 위험함 | 복잡함 | 이해 어려움 | 링킹 오류 |
'임베디드 기초' 카테고리의 다른 글
| 포인터와 배열 - 입문자가 반드시 알아야 할 관계 (0) | 2025.12.29 |
|---|---|
| 자동차 개발자가 반드시 알아야 할 ROM, RAM, Flash, EEPROM, NVM의 완벽한 차이 (1) | 2025.12.22 |
| 개발자가 반드시 알아야 할 Compile, Linking, Build, Rebuild, GenerateAll의 완벽한 차이 (0) | 2025.12.19 |
| 🔗 링크(Link)란 무엇인가? (0) | 2025.12.14 |
| 컴파일(Compile)이란 무엇인가? (0) | 2025.12.14 |