C 언어 static 완벽 가이드 | static 변수·static 함수·내부 링크·전역 변수 차이

C 언어 static 완벽 가이드 | static 변수·static 함수·내부 링크·전역 변수 차이

들어가며: static이 뭐가 다른가요?

“static을 붙이면 뭐가 달라지나요?”

C 프로그래밍을 하다 보면 static 키워드를 자주 마주치게 됩니다. 하지만 같은 static이라도 어디에 붙이느냐에 따라 완전히 다른 의미를 갖습니다.

아래 코드는 c를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 1. 함수 내부 static 변수
void counter() {
    static int count = 0;  // 함수 호출 간 값 유지
    count++;
    printf("%d\n", count);
}

// 2. 파일 범위 static 변수
static int file_var = 10;  // 이 파일에서만 접근 가능

// 3. static 함수
static void helper() {     // 이 파일에서만 호출 가능
    // ...
}

이 글을 읽으면:

  • static 지역 변수와 일반 지역 변수의 차이를 이해할 수 있습니다.
  • static 전역 변수와 일반 전역 변수의 차이를 이해할 수 있습니다.
  • static 함수의 용도와 내부 링크의 개념을 이해할 수 있습니다.
  • 메모리 배치와 초기화 시점을 알 수 있습니다.
  • 실전에서 static을 효과적으로 활용할 수 있습니다.

실무에서 마주한 현실

개발을 배울 때는 모든 게 깔끔하고 이론적입니다. 하지만 실무는 다릅니다. 레거시 코드와 씨름하고, 급한 일정에 쫓기고, 예상치 못한 버그와 마주합니다. 이 글에서 다루는 내용도 처음엔 이론으로 배웠지만, 실제 프로젝트에 적용하면서 “아, 이래서 이렇게 설계하는구나” 하고 깨달은 것들입니다.

특히 기억에 남는 건 첫 프로젝트에서 겪은 시행착오입니다. 책에서 배운 대로 했는데 왜 안 되는지 몰라 며칠을 헤맸죠. 결국 선배 개발자의 코드 리뷰를 통해 문제를 발견했고, 그 과정에서 많은 걸 배웠습니다. 이 글에서는 이론뿐 아니라 실전에서 마주칠 수 있는 함정들과 해결 방법을 함께 다루겠습니다.

목차

  1. static 지역 변수
  2. static 전역 변수
  3. static 함수
  4. 메모리 배치와 초기화
  5. 링크(Linkage) 이해하기
  6. 완전한 예제
  7. 자주 발생하는 실수
  8. 실전 활용 패턴
  9. 정리

1. static 지역 변수

일반 지역 변수 vs static 지역 변수

일반 지역 변수는 함수가 호출될 때마다 스택에 생성되고, 함수가 종료되면 소멸됩니다.

static 지역 변수는 프로그램 시작 시 데이터 영역에 할당되고, 프로그램 종료 시까지 유지됩니다.

다음은 c를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 일반 지역 변수
void normal_counter() {
    int count = 0;  // 매번 0으로 초기화
    count++;
    printf("Normal: %d\n", count);  // 항상 1 출력
}

// static 지역 변수
void static_counter() {
    static int count = 0;  // 최초 1회만 초기화
    count++;
    printf("Static: %d\n", count);  // 1, 2, 3, ... 증가
}

int main() {
    for (int i = 0; i < 3; i++) {
        normal_counter();  // 1, 1, 1
        static_counter();  // 1, 2, 3
    }
    return 0;
}

출력:

아래 코드는 code를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

Normal: 1
Static: 1
Normal: 1
Static: 2
Normal: 1
Static: 3

초기화 시점

다음은 c를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

void test() {
    static int a = 10;      // 프로그램 시작 시 1회만 초기화
    static int b;           // 0으로 자동 초기화
    
    printf("a=%d, b=%d\n", a, b);
    a++;
    b++;
}

int main() {
    test();  // a=10, b=0
    test();  // a=11, b=1
    test();  // a=12, b=2
    return 0;
}

핵심:

  • static int a = 10;컴파일 타임에 데이터 영역에 배치됩니다.
  • 초기화 코드는 최초 1회만 실행됩니다.
  • 명시적 초기화가 없으면 0으로 자동 초기화됩니다.

실전 예제: 함수 호출 카운터

아래 코드는 c를 사용한 구현 예제입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <stdio.h>

void expensive_function() {
    static int call_count = 0;
    call_count++;
    
    if (call_count > 100) {
        fprintf(stderr, "Warning: expensive_function called %d times\n", 
                call_count);
    }
    
    // 실제 작업...
}

2. static 전역 변수

일반 전역 변수 vs static 전역 변수

일반 전역 변수외부 링크(external linkage)를 가져, 다른 파일에서 extern으로 접근 가능합니다.

static 전역 변수내부 링크(internal linkage)를 가져, 선언된 파일 내부에서만 접근 가능합니다.

아래 코드는 c를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// file1.c
int global_var = 100;        // 외부 링크: 다른 파일에서 접근 가능
static int file_var = 200;   // 내부 링크: 이 파일에서만 접근 가능

void func1() {
    printf("global_var=%d, file_var=%d\n", global_var, file_var);
}

아래 코드는 c를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// file2.c
extern int global_var;       // ✅ OK: file1.c의 global_var 접근
// extern int file_var;      // ❌ 에러: static이므로 접근 불가

void func2() {
    printf("global_var=%d\n", global_var);  // OK
    // printf("%d\n", file_var);            // 에러: 정의되지 않음
}

이름 충돌 방지

여러 파일에서 같은 이름의 변수를 사용하고 싶을 때 static을 사용합니다.

아래 코드는 c를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// module_a.c
static int counter = 0;  // module_a 전용

void module_a_increment() {
    counter++;
}

int module_a_get() {
    return counter;
}

아래 코드는 c를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// module_b.c
static int counter = 0;  // module_b 전용 (module_a와 별개)

void module_b_increment() {
    counter++;
}

int module_b_get() {
    return counter;
}

두 파일의 counter완전히 독립적입니다. 링크 시 충돌이 발생하지 않습니다.


3. static 함수

일반 함수 vs static 함수

일반 함수는 외부 링크를 가져, 다른 파일에서 선언(extern)하고 호출할 수 있습니다.

static 함수는 내부 링크를 가져, 선언된 파일 내부에서만 호출 가능합니다.

아래 코드는 c를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// utils.c
// 외부에 공개할 함수
int public_function(int x) {
    return helper(x) * 2;
}

// 내부 헬퍼 함수 (외부에 숨김)
static int helper(int x) {
    return x + 10;
}

아래 코드는 c를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// main.c
extern int public_function(int x);  // ✅ OK
// extern int helper(int x);        // ❌ 링크 에러: static이므로 접근 불가

int main() {
    int result = public_function(5);  // OK
    // int val = helper(5);           // 에러: 정의되지 않음
    return 0;
}

왜 static 함수를 사용하나?

  1. 캡슐화: 내부 구현을 숨기고, 공개 API만 노출
  2. 이름 충돌 방지: 여러 파일에서 같은 이름의 헬퍼 함수 사용 가능
  3. 최적화: 컴파일러가 인라인 등 최적화를 더 공격적으로 수행
  4. 유지보수: 함수가 파일 내부에서만 사용됨을 명확히 표시

다음은 c를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// logger.c
static void format_timestamp(char* buffer, size_t size) {
    // 내부 헬퍼 함수
}

static void write_to_file(const char* message) {
    // 내부 헬퍼 함수
}

// 공개 API
void log_message(const char* message) {
    char timestamp[64];
    format_timestamp(timestamp, sizeof(timestamp));
    write_to_file(message);
}

4. 메모리 배치와 초기화

메모리 영역

C 프로그램의 메모리는 크게 4개 영역으로 나뉩니다:

아래 코드는 mermaid를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

flowchart TB
    subgraph memory[메모리 레이아웃]
        stack["스택\n지역 변수, 매개변수"]
        heap["힙\n동적 할당 malloc"]
        data["데이터 영역\n전역/static 변수 초기화됨"]
        bss["BSS 영역\n전역/static 변수 0 초기화"]
        text["텍스트 영역\n코드"]
    end
    
    stack -->|높은 주소| heap
    heap --> data
    data --> bss
    bss -->|낮은 주소| text

static 변수의 메모리 배치

다음은 c를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <stdio.h>

int global_init = 100;      // 데이터 영역
int global_uninit;          // BSS 영역
static int static_init = 200;   // 데이터 영역
static int static_uninit;       // BSS 영역

void func() {
    int local = 10;             // 스택
    static int static_local = 20;   // 데이터 영역
    static int static_local_uninit; // BSS 영역
    
    printf("Addresses:\n");
    printf("global_init:         %p\n", (void*)&global_init);
    printf("global_uninit:       %p\n", (void*)&global_uninit);
    printf("static_init:         %p\n", (void*)&static_init);
    printf("static_uninit:       %p\n", (void*)&static_uninit);
    printf("local:               %p\n", (void*)&local);
    printf("static_local:        %p\n", (void*)&static_local);
    printf("static_local_uninit: %p\n", (void*)&static_local_uninit);
}

출력 예시 (주소는 실행마다 다를 수 있음):

아래 코드는 code를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

Addresses:
global_init:         0x404040  (데이터)
global_uninit:       0x404050  (BSS)
static_init:         0x404044  (데이터)
static_uninit:       0x404054  (BSS)
local:               0x7ffc1234  (스택)
static_local:        0x404048  (데이터)
static_local_uninit: 0x404058  (BSS)

초기화 규칙

변수 종류초기화 안 함초기화 함메모리 영역
지역 변수쓰레기 값스택에 저장스택
static 지역 변수0데이터 영역데이터/BSS
전역 변수0데이터 영역데이터/BSS
static 전역 변수0데이터 영역데이터/BSS

아래 코드는 c를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// 실행 예제
void test() {
    int a;              // 쓰레기 값 (초기화 안 됨)
    static int b;       // 0 (자동 초기화)
    static int c = 10;  // 10 (명시적 초기화)
    
    printf("a=%d, b=%d, c=%d\n", a, b, c);  // a는 예측 불가
}

일상 비유로 이해하기: 메모리를 아파트 건물로 생각해보세요. 스택은 엘리베이터 같아서 빠르지만 공간이 제한적입니다. 힙은 창고처럼 넓지만 물건을 찾는 데 시간이 걸립니다. 포인터는 “3층 302호”처럼 주소를 가리키는 메모지라고 보면 됩니다.

5. 링크(Linkage) 이해하기

링크의 3가지 종류

  1. 외부 링크 (External Linkage): 다른 파일에서 접근 가능
  2. 내부 링크 (Internal Linkage): 같은 파일 내에서만 접근 가능
  3. 링크 없음 (No Linkage): 선언된 블록 내에서만 접근 가능

아래 코드는 c를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// file.c
int global = 10;           // 외부 링크
static int file_scope = 20; // 내부 링크

void func() {
    int local = 30;        // 링크 없음
    static int static_local = 40;  // 링크 없음 (하지만 수명은 프로그램 전체)
}

extern과 static의 관계

아래 코드는 c를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// module.c
int public_var = 100;       // 외부 링크
static int private_var = 200;  // 내부 링크

void public_func() {        // 외부 링크
    // ...
}

static void private_func() {   // 내부 링크
    // ...
}

아래 코드는 c를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// main.c
extern int public_var;      // ✅ module.c의 public_var 참조
// extern int private_var;  // ❌ 링크 에러: static이므로 접근 불가

extern void public_func();  // ✅ module.c의 public_func 참조
// extern void private_func(); // ❌ 링크 에러

int main() {
    printf("%d\n", public_var);  // OK
    public_func();               // OK
    return 0;
}

6. 완전한 예제

예제 1: 싱글톤 패턴 (C 스타일)

다음은 c를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// singleton.c
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int value;
    char name[64];
} Config;

// static 포인터: 파일 내부에서만 접근 가능
static Config* instance = NULL;

// 내부 헬퍼 함수
static Config* create_config() {
    Config* cfg = (Config*)malloc(sizeof(Config));
    if (cfg) {
        cfg->value = 42;
        snprintf(cfg->name, sizeof(cfg->name), "DefaultConfig");
    }
    return cfg;
}

// 공개 API
Config* get_config() {
    if (instance == NULL) {
        instance = create_config();
    }
    return instance;
}

void destroy_config() {
    if (instance) {
        free(instance);
        instance = NULL;
    }
}

다음은 c를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// main.c
#include <stdio.h>

extern Config* get_config();
extern void destroy_config();

int main() {
    Config* cfg1 = get_config();
    Config* cfg2 = get_config();
    
    printf("Same instance? %s\n", (cfg1 == cfg2) ? "Yes" : "No");  // Yes
    printf("Value: %d\n", cfg1->value);
    
    destroy_config();
    return 0;
}

예제 2: 파일별 독립적인 카운터

아래 코드는 c를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// counter_a.c
#include <stdio.h>

static int counter = 0;  // counter_a 전용

void counter_a_increment() {
    counter++;
}

int counter_a_get() {
    return counter;
}

아래 코드는 c를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// counter_b.c
#include <stdio.h>

static int counter = 0;  // counter_b 전용 (counter_a와 별개)

void counter_b_increment() {
    counter++;
}

int counter_b_get() {
    return counter;
}

다음은 c를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// main.c
#include <stdio.h>

extern void counter_a_increment();
extern int counter_a_get();
extern void counter_b_increment();
extern int counter_b_get();

int main() {
    counter_a_increment();
    counter_a_increment();
    counter_b_increment();
    
    printf("Counter A: %d\n", counter_a_get());  // 2
    printf("Counter B: %d\n", counter_b_get());  // 1
    
    return 0;
}

예제 3: 함수 호출 통계

다음은 c를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <stdio.h>
#include <time.h>

void critical_function() {
    static int call_count = 0;
    static time_t first_call = 0;
    static time_t last_call = 0;
    
    time_t now = time(NULL);
    
    if (call_count == 0) {
        first_call = now;
    }
    
    call_count++;
    last_call = now;
    
    // 실제 작업...
    
    if (call_count % 100 == 0) {
        double elapsed = difftime(last_call, first_call);
        printf("Stats: %d calls in %.0f seconds (%.2f calls/sec)\n",
               call_count, elapsed, call_count / elapsed);
    }
}

7. 자주 발생하는 실수

실수 1: static 지역 변수를 매번 초기화한다고 생각

아래 코드는 c를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ 잘못된 이해
void wrong() {
    static int count = 0;  // "매번 0으로 리셋된다"고 생각
    count++;
    printf("%d\n", count);
}

// ✅ 올바른 이해
void correct() {
    static int count = 0;  // 최초 1회만 초기화, 이후 값 유지
    count++;
    printf("%d\n", count);  // 1, 2, 3, ...
}

실수 2: static 변수의 주소를 반환

아래 코드는 c를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ⚠️ 주의: 안전하지만 의도를 명확히 해야 함
int* get_counter() {
    static int counter = 0;
    return &counter;  // OK: static이므로 함수 종료 후에도 유효
}

// ❌ 위험: 일반 지역 변수의 주소 반환
int* get_local() {
    int local = 10;
    return &local;  // 에러: 함수 종료 후 무효화됨
}

실수 3: static 전역 변수를 extern으로 접근 시도

아래 코드는 c를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// file1.c
static int private_var = 100;

// file2.c
extern int private_var;  // ❌ 링크 에러: static이므로 접근 불가

int main() {
    printf("%d\n", private_var);  // 컴파일은 되지만 링크 실패
    return 0;
}

실수 4: 멀티스레드 환경에서 static 변수 공유

다음은 c를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ⚠️ 스레드 안전하지 않음
void thread_function() {
    static int shared = 0;  // 모든 스레드가 공유
    shared++;  // Race condition 발생 가능
    printf("%d\n", shared);
}

// ✅ 스레드 안전하게 수정
#include <pthread.h>

void thread_safe_function() {
    static int shared = 0;
    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    
    pthread_mutex_lock(&lock);
    shared++;
    printf("%d\n", shared);
    pthread_mutex_unlock(&lock);
}

실수 5: static 배열 크기를 런타임에 결정

아래 코드는 c를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

void wrong(int size) {
    // ❌ 에러: static 배열 크기는 컴파일 타임 상수여야 함
    // static int array[size];
    
    // ✅ 올바른 방법
    static int array[100];  // 고정 크기
    // 또는
    int* dynamic = malloc(size * sizeof(int));  // 동적 할당
}

8. 실전 활용 패턴

패턴 1: 초기화 플래그

아래 코드는 c를 사용한 구현 예제입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <stdio.h>

void initialize_once() {
    static int initialized = 0;
    
    if (!initialized) {
        printf("Performing expensive initialization...\n");
        // 복잡한 초기화 작업
        initialized = 1;
    }
    
    // 실제 작업
}

패턴 2: 캐싱

아래 코드는 c를 사용한 구현 예제입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <string.h>

const char* get_user_name() {
    static char cache[64] = {0};
    static int cached = 0;
    
    if (!cached) {
        // 실제로는 DB나 파일에서 읽어옴
        strcpy(cache, "John Doe");
        cached = 1;
    }
    
    return cache;
}

패턴 3: 상태 머신

다음은 c를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

typedef enum {
    STATE_IDLE,
    STATE_RUNNING,
    STATE_PAUSED,
    STATE_STOPPED
} State;

void state_machine(int event) {
    static State current_state = STATE_IDLE;
    
    switch (current_state) {
        case STATE_IDLE:
            if (event == EVENT_START) {
                current_state = STATE_RUNNING;
            }
            break;
        case STATE_RUNNING:
            if (event == EVENT_PAUSE) {
                current_state = STATE_PAUSED;
            } else if (event == EVENT_STOP) {
                current_state = STATE_STOPPED;
            }
            break;
        // ...
    }
}

패턴 4: 디버그 카운터

다음은 c를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#ifdef DEBUG
#define DEBUG_COUNT(name) \
    do { \
        static int count_##name = 0; \
        count_##name++; \
        if (count_##name % 1000 == 0) { \
            fprintf(stderr, #name " called %d times\n", count_##name); \
        } \
    } while(0)
#else
#define DEBUG_COUNT(name) ((void)0)
#endif

void some_function() {
    DEBUG_COUNT(some_function);
    // 실제 작업...
}

패턴 5: 모듈 캡슐화

다음은 c를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// module.c
// 내부 상태 (외부에 숨김)
static int internal_state = 0;
static char internal_buffer[1024];

// 내부 헬퍼 함수
static void internal_helper() {
    // ...
}

// 공개 API
void module_init() {
    internal_state = 0;
    memset(internal_buffer, 0, sizeof(internal_buffer));
}

int module_process(const char* input) {
    internal_helper();
    // ...
    return internal_state;
}

9. 정리

static의 두 가지 역할

위치역할효과
함수 내부수명 연장지역 변수가 프로그램 종료까지 유지
파일 범위링크 제한변수/함수를 파일 내부로 제한 (내부 링크)

핵심 정리

  1. static 지역 변수

    • 데이터 영역에 할당
    • 최초 1회만 초기화
    • 함수 호출 간 값 유지
    • 초기화 안 하면 0
  2. static 전역 변수

    • 내부 링크 (파일 내부에서만 접근)
    • 다른 파일에서 extern 불가
    • 이름 충돌 방지
  3. static 함수

    • 내부 링크 (파일 내부에서만 호출)
    • 캡슐화, 이름 충돌 방지
    • 컴파일러 최적화 유리
  4. 메모리 배치

    • 초기화된 static: 데이터 영역
    • 초기화 안 된 static: BSS 영역
    • 일반 지역 변수: 스택
  5. 활용 패턴

    • 싱글톤 패턴
    • 초기화 플래그
    • 캐싱
    • 상태 유지
    • 모듈 캡슐화

언제 static을 사용할까?

  • ✅ 함수 호출 간 값을 유지하고 싶을 때
  • ✅ 파일 내부에서만 사용하는 변수/함수일 때
  • ✅ 이름 충돌을 방지하고 싶을 때
  • ✅ 모듈의 내부 구현을 숨기고 싶을 때
  • ❌ 멀티스레드 환경에서 동기화 없이 사용 (주의)
  • ❌ 재진입(reentrant) 함수가 필요할 때

참고 자료

  • C11 Standard (ISO/IEC 9899:2011)
  • “C Programming: A Modern Approach” by K. N. King
  • “Expert C Programming” by Peter van der Linden

자주 묻는 질문 (FAQ)

Q. static 변수는 스레드 안전한가요?

A. 아니요. static 변수는 모든 스레드가 공유하므로, 멀티스레드 환경에서는 뮤텍스 등으로 동기화해야 합니다.

Q. C++의 static과 C의 static이 다른가요?

A. C++에는 추가로 클래스 멤버에 대한 static이 있습니다. 하지만 함수 내부 static과 파일 범위 static의 동작은 C와 동일합니다.

Q. static 변수는 언제 메모리에서 해제되나요?

A. 프로그램이 종료될 때 자동으로 해제됩니다. 명시적으로 해제할 수 없습니다.

Q. static 함수는 인라인될 수 있나요?

A. 네. 오히려 static 함수는 외부 링크가 없어 컴파일러가 더 공격적으로 인라인 최적화를 수행할 수 있습니다.

Q. 헤더 파일에 static 변수를 선언하면?

A. 각 소스 파일마다 독립적인 복사본이 생성됩니다. 일반적으로 권장되지 않습니다.

한 줄 요약: static은 수명 연장(지역 변수)과 링크 제한(전역 변수/함수)의 두 가지 역할을 하며, 캡슐화와 상태 유지에 유용합니다.


관련 글

  • C 메모리 관리 완벽 가이드 | 스택·힙·데이터 영역
  • C 링크와 extern 완벽 가이드
  • C 함수 포인터 완벽 가이드
... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3