[C 시리즈 16편] 저장 기간과 스코프로 변수 수명 읽기

English version

15편에서 전처리기로 코드를 준비했다면, 이제는 "변수가 언제 만들어지고 언제 사라지는가"를 해부해야 합니다. C에서는 같은 이름의 변수라도 저장 기간(storage duration)과 스코프(scope)에 따라 전혀 다른 수명을 가집니다. 여기에 링키지(linkage)까지 더해지면 "언제까지 살아 있는가", "어디서 보이는가", "다른 파일에서도 같은 이름으로 연결되는가"를 따로 생각해야 합니다. 이 흐름을 이해하면 전역 변수 남용을 피하고, 모듈 간 데이터를 주고받을 때도 혼란이 줄어듭니다.

이번 글에서 새로 나오는 용어

  1. 저장 기간 (Storage Duration): 메모리에 값이 얼마나 오래 남아 있는지
  2. 자동 저장 기간 (Automatic Storage): 블록을 벗어나면 사라지는 지역 변수의 수명
  3. 정적 저장 기간 (Static Storage): 프로그램 시작부터 종료까지 유지되는 변수 수명
  4. 스코프 (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는 터미널에서 직접 컴파일하고 다시 실행하는 흐름이 중요하니, 이번 글의 코드를 파일로 만들고 빌드-실행 사이클을 다시 따라가 보세요.

Live Practice

C Practice Sandbox

CodeSandbox

Run the starter project in CodeSandbox, compare it with the lesson code, and keep experimenting.

Universal starterCterminal
  1. starter를 fork한 뒤 hello.c 같은 실습 파일을 만든다
  2. 본문 코드를 붙여 넣고 clang 또는 gcc가 있으면 직접 컴파일한다
  3. 코드를 고친 뒤 다시 빌드하고 실행 결과를 비교한다

C는 브라우저 미리보기보다 터미널 빌드 흐름이 핵심입니다. Universal starter의 컴파일러 구성이 환경에 따라 다를 수 있으니, 먼저 clang이나 gcc 사용 가능 여부부터 확인하세요.

실습

  • 따라 하기: static 지역 변수를 이용해 함수 호출 횟수를 세는 프로그램을 작성하고, 여러 함수에서 각각 독립적으로 카운트가 유지되는지 확인합니다.
  • 확장하기: extern 선언을 헤더 파일로 분리하고, 두 개의 .c 파일이 같은 전역 변수를 공유하도록 빌드해 봅니다.
  • 디버깅: 전역 변수와 같은 이름의 지역 변수를 선언해 의도치 않은 섀도잉(shadowing)이 일어나는지 출력으로 확인합니다.
  • 완료 기준: "자동/정적/동적 저장 기간의 차이"와 static, extern의 역할을 한 문단으로 설명할 수 있으면 충분합니다.

마무리

변수의 수명과 가시성을 명확하게 이해하면, 프로그램이 커져도 상태를 통제하기 쉬워집니다. 지금까지 다룬 메모리, 전처리, 저장 기간을 한 번 더 복습해 두면 이후 함수 포인터와 빌드 자동화를 다룰 때 훨씬 빠르게 연결할 수 있습니다.

💬 댓글

이 글에 대한 의견을 남겨주세요