[C++] 전역 변수, 정적 변수, 내부 연결, 외부 연결
Language/C++

[C++] 전역 변수, 정적 변수, 내부 연결, 외부 연결

뉴비뉴 2026. 5. 10.

제목에 언급한 변수 및 연결을 알아보기 전에 메모리 구조부터 이해하자

C++ 프로그램이 실행되면 메모리는 크게 4구역으로 나뉜다

코드 영역 실행할 명령어들
데이터 영역 전역 변수, static 변수 (프로그램 시작~종료까지 유지)
힙 영역 new로 동적 할당
스택 영역 지역 변수 (함수 호출~종료까지만 존재)

1. 지역 변수 (Local Variable)

void foo() {
    int x = 10;	// 스택에 생성
    			// foo() 끝나면 x 소멸
}
  • 생존 기간: 함수 호출 시 생성, 함수 종료 시 소멸
  • 접근 범위: 해당 함수 안에서만

2. 전역 변수 (Global Variable)

// a.cpp
int g_count = 0;	// 함수 밖에 선언 -> 데이터 영역에 생성

void add() { g_count++; }
void print() { cout << g_count; }	// 어디서든 접근 가능

// b.cpp
extern int g_count;
g_count++;			// 다른 cpp 파일에서도 접근 가능
  • 생존 기간: 프로그램 시작부터 종료까지
  • 접근 범위: 프로그램 전체
단점: 어디서든 수정 가능하기 때문에 버그 추적이 어렵다. 실무에서는 꼭 필요한 경우에만 사용한다.

3. static 변수 - 핵심 개념

static은 "딱 한 번만 초기화하고, 프로그램이 끝날 때까지 유지" 라는 의미입니다

void foo() {
    int local = 0;			// 호출마다 새로 생성, 소멸
    static int stat = 0; 	// 딱 한 번만 초기화, 이후 호출엔 이전 값 유지
    
    local++;
    stat++;
    cout << "local: " << local << ", static: " << stat << endl;
}

foo();	// local: 1, static: 1
foo();	// local: 1, static: 2 <- stat은 이전 값을 기억
foo();	// local: 1, static: 3

 

비유: local은 배번 새 종이에 쓰고 버리는 메모지, static은 계속 누적해서 쓰는 노트
  • 저장 위치는 데이터 영역 (전역 변수와 동일)
  • 하지만 접근 범위는 여전히 그 함수 안에서만

4. 내부 연결 vs 외부 연결

여기서 "연결(linkage)"이란 "이 변수/함수를 다른 파일에서도 쓸 수 있는가?"를 의미한다

내부 연결 (Internal Linkage)

이 파일 안에서만 사용 가능
// a.cpp
static int x = 10;	// static -> 내부 연결, a.cpp 안에서만 존재
// 또는
const int y = 20; 	// const도 기본적으로 내부 연결

void printA() {
    x++;
    cout << "a.cpp x: " << x << endl;
}

a.cpp의 x는 a.cpp에서만 보이고 사용할 수 있다
b.cpp의 x는 b.cpp에서만 보임 (완전히 별개의 본사본)

외부 연결 (External Linkage)

다른 파일에서도 함께 사용 가능
// a.cpp
int x = 10;		// 일반 전역 변수 -> 외부 연결
extern const int y = 20; // extern -> 외부 연결

void printA() {
    x++;
    cout << "a.cpp x: " << x << endl;
}

// b.cpp
extern int x;	// a.cpp의 x를 가져다 씀

void printB(){
    x++;
    cout << "b.cpp x: " << x << endl;
}

// main.cpp
int main() {
    printA();	// a.cpp x: 11
    printA();	// a.cpp x: 12
    printB();	// b.cpp x: 13 <- 같은 x를 공유하기 때문
    printB();	// b.cpp x: 14
}

// ---
내부 연결 (static)          외부 연결 (extern)

a.cpp → [x: 10]            a.cpp ──┐
                                    ├── [x: 10]
b.cpp → [x: 10]            b.cpp ──┘
(각자 따로)                 (하나를 공유)

5. 실전 - 헤더로 상수 공유하기

방법 A: 헤더에 직접 정의 (내부 연결)

// game_config.h
#pragma once
namespace Config {
    const int MAX_PLAYERS = 4;		// include한 파일마다 복사본 생성
    const float GRAVITY = 9.8f;		// 파일이 많아질수록 메모리 낭비
}

// physics.cpp
#include "game_config.h"
#include <iostream>

void applyGravity() {
    // physics.cpp만의 GRAVITY 복사본 사용
    std::cout << "Physics GRAVITY 주소: " << &Config::GRAVITY << std::endl;
}

// main.cpp
#include "game_config.h"
#include  <iostream>

int main() {
    // main.cpp만의 GRAVITY 본사본 사용
    std::cout << "Main GRAVITY 주소 : " << &Config::GRAVITY << std::endl;
    applyGravity();
    // 출력
    // Main		GRAVITY 주소: 0x100 <- 서로 다름
    // Physics	GRAVITY 주소: 0x200 <- 서로 다름
}

방법 B: extern + .cpp 분리 (외부 연결)

실무에서 방법 B를 쓰는 이유:
- 설정 값이 많아질수록 방법 A는 파일 수 만큼 복사본이 생겨 메모리 낭비
- 방법 B는 game_config.cpp 한 곳만 수정하면 전체에 반영
- 큰 프로젝트(파일 수십 개)에서 차이가 두드러진다
// game_config.h
#pragma once
namespace Config {
    extern const int MAX_PLAYERS;	// 선언만 -> "이 변수는 다른 파일에 정의되어 있다"
    extern const float GRAVITY;		// extern만 붙이면 const도 외부 연결로 바뀜
}

// game_config.cpp
#include "game_config.h"

namespace Config 
    extern const int MAX_PLAYERS = 4;	// 실제 정의 -> 메모리에 딱 한 번만 생성
    extern const float GRAVITY = 9.8;	// 모든 파일이 이 하나의 변수를 공유
}

// physics.cpp
#include "game_config.h"  // 선언만 가져옴, 정의는 game_config.cpp에 있음
#include <iostream>

void applyGravity() {
    // game_config.cpp의 GRAVITY를 참조 → main.cpp와 주소 동일
    std::cout << "Physics GRAVITY 주소: " << &Config::GRAVITY << std::endl;
}
// main.cpp
#include "game_config.h"
#include <iostream>

int main() {
    // game_config.cpp의 GRAVITY를 참조 → physics.cpp와 주소 동일
    std::cout << "Main GRAVITY 주소: " << &Config::GRAVITY << std::endl;
    applyGravity();
    // 출력
    // Main 	GRAVITY 주소: 0x100 <- 같음
    // Physics 	GRAVITY 주소: 0x100 <- 같음
}

 

댓글

💲 추천 글