/

이번 글에서는 최근 공부하면서 특히 질문이 많던 Scope, Lifetime, static 지역 변수, const, constexpr를 정리한다. 핵심 “이 변수를 어디에서 사용할 수 있는가?”, “언제까지 살아 있는가?”, “값 바꿀 수 있는가?”를 서로 다른 질문로 보는 것이다.

1.5 Scope and Lifetime — 보이는 범위와 살아 있는 시간은 다르다

Scope란 무엇인가

Scope 어떤 름을 드의 어디에서 사용할 수 있는지를 뜻한다. 지역 변수는 선언된 블록 안에서 이름을 사용할 수 있다.

void f()
    {
        int count = 10;
            std::cout << count;
            }
            
            int main()
            {
                // std::cout << count;  // compile error
                }

countf() 의 로컬 변수이므로 main()에서 사용할 수 없다. 최근 문제에서도 “main scope에서 count는 사용할 수 없다”고 정리했다.

중첩된 블록의 Scope

int main()
    {
        int a = 10;
            {
                    int b = 20;
                            // a: 사용 가능
                                    // b: 사용 가능
                                        }
                                            // a: 사용 가능
                                                // b: 사용 불가
                                                }

안쪽 블록에서는 바깥 변수 a를 볼 수 있지만 바깥에는 안쪽에서 선언한 b를 볼 수 없다.

Lifetime이란 무엇인가

Lifetime은 객체가 실제로 존재하는 기간이다. 일반적인 지역 변수는 함수나 블록에 들어올 때 생성되고 그 블록을 벗어나면 수명이 끝난다.

void test()
    {
        int x = 10;  // lifetime 시
        }                // lifetime 종료

static 지역 변수: Scope와 Lifetime을 분리해서 보기

void test()
    {
        static int count = 10;
            ++count;
                std::cout << count << '\n';
                }

count라는 이름은 test() 안에서만 보인다. 따라서 scope는 local이. 하지만 함수 호출이 끝나도 객체의 값은 유지된다.

test(); // 11
    test(); // 12
    test(); // 13

최근 학습에서 “static이니까 값이 유지되어 13이 된다”고 이해했다. 더 정확히 현하면 이름의 scope는 지역이지만 객의 lifetime은 계속 유된다.

자주 헷갈리는 질문:
static 지역 는 global 변수인가?
아니다. 함수 밖에서 이름을 사용할 수 없으므로 scope는 local이다. 다만 lifetime이 길다.
구분일반 지역 변수static 지역 변수
이름을 볼 수 있는 범위선언된 블록선언된 블록
함수 종료 후 값 유지아니오
다음 호출에서 이전 값 사용아니오

임베디드 코드에서 static 지역 변수를 쓸 때

함수 내부 상태를 외부에 노출하 않으면서 호출 사에 값을 유지할 때 유용할 수 있. 지만 멀티스레드, ISR, 재진입 가능한 함수에서는 공유 상태처럼 동작할 수 있으므로 동시 근과 재진입성도 검토해야 한다.

1.5 핵심: Scope는 이름이 보이는 범위, Lifetime은 체가 존하는 기간이다. static 지역 변수는 local scope와 긴 lifetime을 동시에 가진다.

1.6 Constants — 바뀌지 않는 값을 타입 시스템으로 표현기

const의 기본 의미

const int a{10};
    // a = 20;   // compile error

a는 성될 때 10으로 초기화되고 이후 다 값을 대입할 수 없다. 최근 문제에서 “이미 처음에 a가 10으로 초기화된 이후라서 변경할 없다”고 답했던 부분이 바로 이 개념이다.

const는 반드시 컴파일 타임 상수인가

그렇지 않다. const 객체는 런타임 값으로 초기될 수도 있다.

int read_value();
    const int value = read_value();

read_value()의 결과는 실행 중에 결정될 수 있지만, 한 번 value가 초기화되고 나면 이후 수정할 수 없다.

constexpr는 무엇이 다른가

constexpr int buffer_size = 256;
    int buffer[buffer_size];

constexpr는 해당 값이 상수 표현식으로 사용될 수 있음을 나타낸다. 컴파일 타임 계산이 필요한 곳에 적합하다.

구분constconstexpr
초기화 후 변경불가불가
런타임 값로 초기화가능한 경우 있일반적으 불가
컴파일 타임 상 표현식항상 보장하지 않음주요 목적

const와 초기화의 관계

const int max_retry{3};

const 객체는 나중에 값을 넣는 것이 아니라 선언할 때 적절한 값으로 초기화하는 것이 핵심이다.

임베디드에서 const / constexpr가 유용한 곳

constexpr std::uint32_t STATUS_REG = 0x4000020;
    constexpr std::uint16_t CAN_PERIOD_MS = 250;
    constexpr std::size_t RX_BUFFER_SIZE = 256;

레지스터 주소, CAN 주기, 고정 퍼 크기처럼 실행 바뀔 이유가 없는 값은 상수로 표현하는 편이 읽기 쉽고 안전하다. 숫자를 코드 곳곳 직 쓰는 것보다 이름과 단위를 함께 드러 수 있다.

상수를 쓰는 이유는 변 방지뿐인가

아니다. 상수는 코드 리뷰와 유지보수에도 도움을 준다. 어떤 값이 상태인지, 어떤 값이 설정인지, 어떤 값이 절대 바뀌면 안 되는 규칙인지 구분하기 쉬워진다.

1.6 핵심: const는 초기화 이후 변경하지 않는 객체를 표현하고, constexpr는 컴파일 타임 상수 표현식이 필요한 상황까지 명확하게 표현한다.

1.5 1.6을 함께 보면

  1. 이 변수의 타입은 무엇인가?
  2. 이 이름은 어디까지 보이는가? → Scope
  3. 객체는 언제까지 살아 있는가? → Lifetime
  4. 초기화 후 값을 바꿀 수 있는가? → const / constexpr

이 네 가지를 함께 보면 실무 C++ 코드의 상태 변화가 훨씬 선명하게 보다.

마무리

1.5~1.6은 문법 자체보다 변수의 존재 방식과 상태를 이해하는 챕터라고 느꼈다. static 지역 변수를 단순히 “전역처럼 값이 남는 변수”라고 외우기보다 local scope와 긴 lifetime을 분리해 이해하는 것이 중하다. const와 constexpr도 단순 키워드가 아니라 변경 가능성과 컴파일 타임 의도를 코드에 표현하는 수단이다.

1.5 Scope and Lifetime

Scope describes where a name can be used. Lifetime describes how long the object itself exists. A normal local variable usually disappears when its block ends.

Static local variables

A static local variable still has local scope, but its stored value survives after the function returns. It is not the same as a global variable because the name remains local to the function.

1.6 Constants

const prevents modification after initialization, while constexpr is intended for values usable in compile-time constant expressions.

const int a{10};
    constexpr int buffer_size = 256;

Embedded perspective

Register addresses, protocol constants, timing values, and fixed buffer sizes are natural candidates for named constants. They make hardware-facing code easier to review and reduce accidental modification.

Takeaway

When reading a variable, ask four questions: what is its type, where is its name visible, how long does the object exist, and can its value change?

+ Recent posts