변수, 함수, 클래스가 보여지고 사용될 수 있는 영역을 의미한다.
같은 영역내에 같은 이름이 있으면 이름이 충돌할 수 있다.

  • Block : 중괄호 내부에서만 사용
    { int x; }
    void f(int b) { int a; } // a와 b모두 Block Scope에 해당
  • Function : 함수 내부에서만 사용, Block과 다르다. label 전용
    void f()
    {
    label:
       goto label;
    }
  • Class : 클래스 내부에서만 사용
    class A{ int x; }
  • Namespace : namespace 내부
    namespace N { int x; }
  • Global : 파일 최상위 공간, Global도 namespace의 일종
    int x;

Linkage, Storage Duration


Linkage : 다른 번역 단위에서 동일한 개체인가를 결정

  • No Linkage : 다른 곳과 연결 안됨, 지역 변수, 함수 매개변수

    void f() { int x ; // no linkage }
  • Internal Linkage : 같은 번역 단위 내부에서만 연결

    static int x;
    namespace n { int x; }
  • External Linkage : 다른 번역 단위와 연결 가능
    External Linkage인 변수의 정의가 없으면 unresolved external error 발생

    //A.cpp
    int x;
    
    //B.cpp
    extern int x;

Storage Duration : instance가 언제 생성/파괴 되는지

  • Automatic : 자동 생성/파괴
    // Stack
    void f() { int x; }
  • Static : 프로그램 생명 주기와 같은 생성/파괴
    static int x;
  • Thread Storage Duration : 스레드 생명 주기와 같은 생성/파괴
    thread_local int x;
  • Dynamic : Heap 메모리 사용 할당/해제
    int* p = new int;

구분 질문 결정 시점
Scope 이름이 어디서 보이는가? 컴파일 타임
Linkage 다른 번역 단위와 같은 이름으로 연결되는가? 링킹
Storage Duration instance가 언제 생성되고 언제 파괴되는가? 실행 시

class A
{
    static int x;
}

Scope : Class
Linkage : External
Storage Duration : Static

void f()
{
    static int x;
}

Scope : Block
Linkage : No Linkage
Storage Duration : Static

static int x;

Scope : namespace (Global)
Linkage : Internal
Storage Duration : Static


static

Data 영역에 저장되어, 선언된 위치에 따라 접근범위가 달라지지만, 사라지지 않는다.
최초 1회만 초기화가 가능하다. 저장된 메모리는 프로그램 끝까지 유지한다.

static 없이 정의된 전역 변수를 헤더에 두고 여러 번역 단위에서 #include하면
각 번역 단위마다 동일 이름의 외부 연결(external linkage) 정의가 생성된다.
이 경우 컴파일은 각각 성공하지만, 링크 단계에서 동일 전역 심볼의
multiple definition 링크 오류가 발생한다.

// Header
int a;

// A.cpp
#include "Header.h"
// int a 생성 multiple definition

// B.cpp
#include "Header.h"
// int a 생성 multiple definition

static(file/namespace scope)internal linkage를 부여하여
각 번역 단위에 독립된 전역 개체를 생성하고,
링커의 전역 symbol table에 노출되지 않게 한다.
따라서 여러 .obj 파일에 동일 이름이 존재해도
전역 symbol 충돌이 발생하지 않는다.

// Header
static int a;

// A.cpp
#include "Header.h"
//static int a 생성, internal이라 충돌나지 않음

// B.cpp
#include "Header.h"
// static int a 생성, internal이라 충돌나지 않음

external 전역 중복 → 링크 에러
internal 전역 중복 → 독립 개체


전역변수도 기본적으로 프로그램 시작 시 생성되고, 종료 시 소멸한다.
static과 똑같이 .bss, .data영역을 사용한다.

아래 내용부터 .obj파일 분석에는 dumpbin을 사용했다.

static int static_data_init[1000] = {1,2,};
int extern_data_init[1000] = { 1,2, };

static int static_data[1000];
int extern_data[1000];
_DATA    SEGMENT
?static_data_init@@3PAHA DD 01H                ; static_data_init
    DD    02H
    ORG $+3992
_DATA    ENDS

_DATA    SEGMENT
?extern_data_init@@3PAHA DD 01H                ; extern_data_init
    DD    02H
    ORG $+3992
_DATA    ENDS

_BSS    SEGMENT
?static_data@@3PAHA DD 03e8H DUP (?)            ; static_data
_BSS    ENDS

_BSS    SEGMENT
?extern_data@@3PAHA DD 03e8H DUP (?)            ; extern_data
_BSS    ENDS

여기서 Linkage 차이를 주기 위해서 static 변수를 사용한다.

// 섹션(SECT) 설명 줄은 한번만 출력 됨
011 00000000 SECT5  notype       Static       | .data
    Section length 1F40, #relocs    0, #linenums    0, checksum F276EEDE
    Relocation CRC 00000000
014 00000000 SECT5  notype       External     | ?extern_data_init@@3PAHA (int * extern_data_init)

015 00000000 SECT6  notype       Static       | .bss
    Section length 1F40, #relocs    0, #linenums    0, checksum        0
    Relocation CRC 00000000
018 00000000 SECT6  notype       External     | ?extern_data@@3PAHA (int * extern_data)

1CE 00000FA0 SECT5  notype       Static       | ?static_data_init@@3PAHA (int * static_data_init)
1CF 00000FA0 SECT6  notype       Static       | ?static_data@@3PAHA (int * static_data)

static_data_init.data, static_c.bss에 들어갔다

전역변수는 External로, static/internal변수는 Static으로 symbol이 나타난다.
이 symbol을 통해 링커가 구분해서 링킹한다.


초기화 하지 않은 static값은 .bss 영역에 데이터 크기가 0인 상태로 저장된다.
초기화 한 static값은 .data영역에 올라간다.

static int a[1000];
static int b[1000] = { 1, 2 };

int c[1000];
int d[1000] = { 1, 2 };

머신 코드를 보면

_BSS    SEGMENT
?c@@3PAHA DD    03e8H DUP (?)                ; c
_BSS    ENDS

...

_BSS    SEGMENT
?a@@3PAHA DD    03e8H DUP (?)                ; a
_BSS    ENDS

컴파일러가 무슨 값 인지 모르니까 기록하지 않는다.

_DATA    SEGMENT
?d@@3PAHA DD    01H                    ; d
    DD    02H
    ORG $+3992
_DATA    ENDS

...

_DATA    SEGMENT
?b@@3PAHA DD    01H                    ; b
    DD    02H
    ORG $+3992
_DATA    ENDS

첫번째와 두번째를 1과 2로 채우고 나머지 공간 3992byte를 확보하고 있다.
a의 주소값으로 검색해보면 0으로 싹 밀려있다.

.cod_BSS, _DATA가 분리되어 보이는 것은
정적 instance가 초기화 여부에 따라 서로 다른 섹션에 배치되기 때문이다.
정적 instance는 스택처럼 런타임에 생성되는 것이 아니라
컴파일 시 섹션(.data/.bss)에 배치되고,
링커가 최종 실행 이미지에서 절대 주소를 결정한다.

컴파일 단계에서는 섹션 내 상대 오프셋만 정해지며
정렬(alignment)과 패딩에 따라 변수 위치가 조정된다.

또한 컴파일러는 internal linkage(static)와 external linkage 전역을
서로 다른 심볼로 구분하여 링커가 전역 심볼 충돌을 처리하기 쉽게 한다.


static 변수는 프로그램 시작 시 정적 영역에 메모리가 할당되며,
static 지역 변수는 해당 코드가 처음 실행될 때 초기화가 수행된다.

class A
{
public:
    static int data;
    const static int c_data = 97;

protected:
    static int data1;

protected:
    static int data2;
};

void func()
{
    static int f_data;
    cout << f_data; // 생략못하게 사용
}
int A::data = 5;
int A::data1 = 50;
func();

static멤버 변수들은 선언만으로는 영역에 올라가지 않는다.
정의가 있어야 비로소 저장된다.

_DATA    SEGMENT
?data@A@@2HA DD    05H                    ; A::data
?data1@A@@1HA DD 032H                    ; A::data1
_DATA    ENDS

클래스 내부 변수의 외부 정의는 컴파일러가 .data에 바이트를 만들고 사용한다.

c_data는 상수로 치환되어 데이터로 보이지도 않는다.
명시적으로 사용해보면

cout << A::c_data;
cout << A::data;
0001b    ba 61 00 00 00     mov     edx, 97            ; 00000061H

이런식으로 97이라는 상수값이 사용된다.
별도의 데이터 영역을 생성하지 않는다.

data2는 선언만 있고 정의가 없기 때문에 .cod에 올라오지 않았다.

f_data_BSS에 잘 올라왔다.

_BSS    SEGMENT
?f_data@?1??func@@YAXXZ@4HA DD 01H DUP (?)        ; `func'::`2'::f_data
_BSS    ENDS

static 함수, static 멤버 함수

함수는 Code영역에 저장된다.

static 멤버 함수는 instance에 속하지 않기 때문에 this가 없다.
static 멤버 함수에는 virtual이 붙을 수 없다. 컴파일 에러가 발생한다.
virtual 함수는 instance의 동적 타입에 따라 dispatch되며 this 포인터가 필요하지만,
static 멤버 함수는 instance에 속하지 않아 this가 존재하지 않으므로 virtual이 될 수 없다.

class A
{
public:
    static void sfunc()
    {
        cout << "Do something\n";
    }
    virtual void vFunc()
    {
        cout << "Do something vFunc\n";
    }
    void func()
    {
        cout << "Do something func\n";
    }
    //virtual static void vsFunc()
    //{
    //    cout << "Do someting vsFunc\n";
    //}
};
class B : public A
{

};

static void sgFunc()
{
    cout << "Do something sgFunc\n";
}
void gFunc()
{
    cout << "Do something gFunc\n";
}

sfunc()

_TEXT    SEGMENT
?sfunc@A@@SAXXZ PROC                    ; A::sfunc, COMDAT
; Line 9
$LN3:
  ; 함수 내용 생략
?sfunc@A@@SAXXZ ENDP                    ; A::sfunc
_TEXT    ENDS

vfunc()

_TEXT    SEGMENT
?vFunc@A@@UEAAXXZ PROC                    ; A::vFunc, COMDAT
; Line 13
$LN3:
  ; 함수 내용 생략
?vFunc@A@@UEAAXXZ ENDP                    ; A::vFunc
_TEXT    ENDS

func()

_TEXT    SEGMENT
this$ = 224
?func@A@@QEAAXXZ PROC                    ; A::func, COMDAT
; Line 17
    ; 함수 내용 생략
?func@A@@QEAAXXZ ENDP                    ; A::func
_TEXT    ENDS

sgFunc()

_TEXT    SEGMENT
?sgFunc@@YAXXZ PROC                    ; sgFunc, COMDAT
; File F:\gameClient\cpp\Test\Test\Scope.cpp
; Line 31
  ; 내용 생략
?sgFunc@@YAXXZ ENDP                    ; sgFunc
_TEXT    ENDS

gFunc()도 Code(Text)영역에 만들어진다.


static 함수는 Code영역에서 만들어지지만 링커가 다른 파일과 연결하려는 것을 컴파일러가 제한한다.

static 멤버 함수는 Instance에 속하지 않는 함수이므로 this 포인터 자체가 존재하지 않기때문에,
instance없이 호출이 가능하다.

int main()
{
    B b;
    b.sfunc();
    B::sfunc();    // `instance` 없이 호출

    b.vFunc();
    b.func();

    sgFunc();
    gFunc();

    return 0;
}
; Line 41
  00046    e8 00 00 00 00     call     ?sfunc@A@@SAXXZ        ; A::sfunc
; Line 42
  0004b    e8 00 00 00 00     call     ?sfunc@A@@SAXXZ        ; A::sfunc
; Line 44
  00050    48 8d 4d 08     lea     rcx, QWORD PTR b$[rbp] ; B this 호출
  00054    e8 00 00 00 00     call     ?vFunc@A@@UEAAXXZ    ; A::vFunc
; Line 45
  00059    48 8d 4d 08     lea     rcx, QWORD PTR b$[rbp] ; B this 호출
  0005d    e8 00 00 00 00     call     ?func@A@@QEAAXXZ    ; A::func
; Line 47
  00062    e8 00 00 00 00     call     ?sgFunc@@YAXXZ        ; sgFunc
; Line 48
  00067    e8 00 00 00 00     call     ?gFunc@@YAXXZ        ; gFunc
; Line 50

.obj를 까보자.
dumpbin.exe를 이용해 SYMBOLS가 표시되게 했다.

1BA 00000000 SECT35 notype ()    External     | ?sfunc@A@@SAXXZ (public: static void __cdecl A::sfunc(void))
1BB 00000000 SECT39 notype ()    External     | ?vFunc@A@@UEAAXXZ (public: virtual void __cdecl A::vFunc(void))
1BC 00000000 SECT2F notype ()    External     | ?func@A@@QEAAXXZ (public: void __cdecl A::func(void))
1BD 00000000 SECT12 notype ()    External     | ??0A@@QEAA@XZ (public: __cdecl A::A(void))

1BF 00000000 SECT14 notype ()    External     | ??0B@@QEAA@XZ (public: __cdecl B::B(void))
1C0 00000000 SECT37 notype ()    Static       | ?sgFunc@@YAXXZ (void __cdecl sgFunc(void))
1C1 00000000 SECT31 notype ()    External     | ?gFunc@@YAXXZ (void __cdecl gFunc(void))

Class내부 static함수는 External, 전역 static 함수는 Static(Internal)이라고 뜬다.
Static이라고 뜨는 함수는 이 파일 내에서만 사용 가능하다.


static 멤버 변수

static 멤버 변수는 클래스 타입의 instance가 아니라 클래스 타입 자체에 속한 전역 개체이다
static 멤버 변수는 클래스가 공유하는 전역 변수이다. 다른 static 값과 마찬가지로 Data영역(.bss/.data)에 저장된다.
External Linkage이고, Storage duration은 프로그램 생성/종료 시점과 같다.

Class 내부에서 선언을 할 순 있지만 정의를 할 수는 없다.
Classinstance가 되는 시점은 런타임이기 때문에, 프로그램 실행과 같이 생성되어야하는
static을 초기화할 수 없다. 밖에서 명시적으로 정의 해줘야한다.

class A { static int data; };

int A::data = 10;

정의는 접근제어자에 막혀도 가능하다.


문법상 instance로 접근할 수 있지만, 실체는 Class namespace로 접근한다.

A* a = new A();
A stackA;

cout << stackA.data << '\n';
cout << a->data;
00         mov     edx, DWORD PTR ?data@A@@2HA ; A::data
00         mov     edx, DWORD PTR ?data@A@@2HA ; A::data

정수형 static const는 내부에서 초기화 가능하다.

class A 
{
public:
    static const int k = 97; // 가능(정수 상수식)
};

static const의 주소를 취할 경우 정의가 따로 필요할 수 있음.

const int* p = &A::k;  // ODR-use
// 이 경우(특히 C++17 이전) 밖에 정의가 필요할 수 있음
const int A::k;        // (초기화는 클래스 안에 있음)

extern

다른 번역 단위에 정의되어 있다는 선언, extern으로 정의하면 메모리를 만들지 않음.
링커가 최종 연결한다. 심볼을 External로 바꾸어 링커가 Exteranl linkage하게 만듬
ODR(One Defintion Rule, 정의는 하나만)을 지켜야한다.

// A.cpp
int g;    // 정의

// B.cpp
extern int g; // A의 g 선언

함수의 symbol은 기본적으로 External이다.

void f()
{
    int nolinkage_data = 0;
    cout << nolinkage_data << '\n';
}

void g()
{
    int nolinkage_data = 0;
    cout << nolinkage_data << '\n';
}
19E 00000000 SECT2D notype ()    External     | ?f@@YAXXZ (void __cdecl f(void))
1A0 00000000 SECT2F notype ()    External     | ?g@@YAXXZ (void __cdecl g(void))

extern이지만 정의라서 메모리에 생성되는 경우

extern int data = 10;
_DATA    SEGMENT
?data@@3HA DD    0aH                    ; data
_DATA    ENDS

// ExternalHeader.h
int data2 = 10;

///////////////////////////
///////////////////////////

#include "ExternalHeader.h"
extern int data = 10;
extern int data2;    // 여기서 정의하면 multi definition
_DATA    SEGMENT
?data2@@3HA DD    0aH                    ; data2
?data@@3HA DD    0aH                    ; data
_DATA    ENDS
009 00000000 SECT3  notype       Static       | .data
    Section length    8, #relocs    0, #linenums    0, checksum FE7DFCAC
    Relocation CRC 00000000
00C 00000000 SECT3  notype       External     | ?data2@@3HA (int data2)
00D 00000004 SECT3  notype       External     | ?data@@3HA (int data)

전역변수가 있는 ExternalHeader.hinclude만해도 데이터가 잡힌다.


constconstexpr은 기본적으로 Interanl이다. Internalextern을 명시적으로
붙여주면 외부에서 사용가능하다.

extern const int data = 10;

혹은
// 헤더
extern const int data;

// Cpp
const int data = 10;

inline

inline 변수는 여러 번 정의가 허용된다.
프로그램 전체에서 하나의 instance로 취급된다.
여러 번역 단위에 정의가 생기가 한다.

// header.h
int x = 10;   // 여러 cpp에서 include할 수 없음.

여러 cpp에서 쓸 수 있게 extern으로 지정

// header.h
extern int data;

// A.cpp
int data = 10;

// B.cpp
void f() { return data; }

externHeader에서 정의를 하는 순간 Multi Definition 이 발생한다.

// ExternalHeader.h
extern int data = 10;

// A.cpp
#include "ExternalHeader.h"
void funcA() { cout << data; } // X

// B.cpp
#include "ExternalHeader.h"
void funcB() { cout << data; } // X

// "int data" (?data@@3HA) already defined in A.obj

#include로 가져오면 텍스트 extern int data = 10;을 복사해오기 때문에
선언이 여러개 생기게 된다.


inline을 쓰면 이 문제를 해결 할 수 있다.

// ExternalHeader.h
inline int data = 10;

// A.cpp
#include "ExternalHeader.h"
void funcA() { cout << data; }

// B.cpp
#include "ExternalHeader.h"
void funcB() { cout << data; }

inline의 symbol을 보면 selection 2 (pick any)라는 symbol이 있다.

0E5 00000000 SECT1B notype       Static       | .data
    Section length    4, #relocs    0, #linenums    0, checksum 6FBDE064, selection    2 (pick any)
    Relocation CRC 00000000
0E8 00000000 SECT1B notype       External     | ?data@@3HA (int data)

이 symbol은 중복 가능하고 하나만 선택하라는 의미이고, 링커는 동일 symbol을 하나만 유지한다.
해당 symbol을 사용하는 모든 변수는 주소가 동일해진다.

inline을 쓰게 되면 헤더에서 정의가 가능하고 주소가 동일하게 유지된다.
이 때문에 최적화를 위해 헤더에서 정의되는 함수, 변수는 inline을 명시적으로 붙여준다.

언어 규칙에 따라 자동 inline처리 될 수 있다.

  • 클래스 내부 정의 함수
  • constexpr 함수
  • consteval 함수
  • 템플릿 함수
  • 템플릿 static 멤버

inline 함수

일반 함수 호출을 최적화 하게 만듬.

int add(int a,int b){ return a+b; }

int f()
{
    return add(1,2);
}

// 일반 함수 호출 시
push 2
push 1
call add

add:
  mov eax, [a]
  add eax, [b]
  ret

inline을 사용하면 컴파일러가 함수 호출 자체를 없애고,
함수 본문을 호출 위치에 삽입한다.

// return add(1,2)를 최적화
int f()
{
    return 1+2;
}

아예 어셈블리 단계에서 처리하기도 한다.

mov eax, 3

inline은 동일 정의의 다중 포함을 허용하도록 ODR(One Definition Rule)을 완화하고, inlining이 가능하면 inlining을 발동 한다.
inlining은 컴파일러가 함수의 호출을 제거하고 함수 본문을 호출 위치에 삽입하는 것을 말한다.


inline을 하면 inlining이 발생할 수 있지만 강제는 아니라 컴파일러가 판단 후 결정한다.
컴파일러가 함수에 inline을 붙여도 inlining하지 않는 경우도 있다.

  • 함수가 크다
  • 디버그로 작동 시
  • 최적화 꺼짐
  • 재귀 사용
  • 주소 사용

이 때 개발자가 __forceinline(UE : FORCEINLINE)를 통해 inline하게 요청할 수 있음

__forceinline != inline
__forceinlineinlining을 강하게 요청하는 문구. inline키워드 없이 __forceinline만 쓰일 수 있다.


선언 storage linkage ODR 섹션 TU 개수
int x; static external 1 정의 .data/.bss 1
extern int x; static external 선언 없음 0
static int x; static internal TU별 .data/.bss TU마다
const int x; static internal TU별 .rdata TU마다
constexpr int x; static internal TU별 .rdata TU마다
inline int x; static external 다중OK COMDAT 1
inline constexpr int x; static external 다중OK COMDAT(.rdata) 1

namespace

이름 충돌을 방지하기 위한 이름 공간.

int n = 0;
namespace space { int n = 0;}
// 동일 공간에서 사용가능
cout << n << ' ' << space::n;

보통의 namespacelinkage를 바꾸지 않지만,
익명 namespaceinternal linkage로 symbol이 결정된다.

namespace static
목적 이름 그룹 TU 제한
scope namespace namespace
linkage external internal
충돌 방지 이름 가시성

inline namespace(C++11)

내부 namespace를 바깥 namespace에 자동으로 노출하는 기능

namespace N
{
    namespace v1
    {
        void f();
    }
}

N::v1::f();
namespace N
{
    inline namespace v1
    {
        void f();
    }
}

N::f();      // 가능
N::v1::f();  // 가능

아래 코드와 개념적으로 동일한 효과를 가진다.

namespace N
{
    inline namespace v1
    {
        void f();
    }

    using namespace v1;
}

보통 버전에 따른 namespace가 존재하고 관리할 때 사용된다.

namespace lib
{
    inline namespace v2
    {
        void api();
    }
}

lib::api();   // v2 사용

나중에 따로 쓰게 막고싶다면 inline 키워드만 제거하면 된다.


{}, Block Scope

{ int x; }

void f() { int x; }

둘다 모두 Block Scope이다.
내부 변수는 모두 linkage가 없는 지역변수이고, Stack에서 메모리 관리가
이루어지기 때문에 storage duration도 자동으로 이루어진다.

다만 static을 붙일 시에 저장공간이 Stack에서 Data영역으로 바뀐다.
inline을 쓸 경우 컴파일 에러 발생.
Error C7524 : 'inline' specifier cannot appear on a block-scope declaration or non-static data member

inline

  • 여러 TU 정의 허용
  • 링커 병합
  • ODR 완화

Block Scope

  • TU 간 공유 없음
  • ODR 문제 없음
  • 링커 병합 대상 아님
선언 위치 scope linkage storage
block int x; block none automatic
block static int x; block none static
block thread_local int x; block none thread
block constexpr int x; block none automatic
block extern int x; block external static

문제

객관식 10문제

https://gemini.google.com/share/35fedb51ff42

주관식 10문제

1
다음 선언의 scope / linkage / storage duration을 각각 설명하세요.

void f()
{
    static int x = 0;
}

그리고 왜 x가 다른 번역 단위와 충돌하지 않는지도 설명하세요.


2
다음 두 코드는 각각 어떤 링크 결과를 만들며 왜 다른가요?

// header.h
int x = 10;
// header.h
static int x = 10;

그리고 obj 심볼 관점에서 어떻게 다르게 나타나는지도 설명하세요.


3
다음 상황에서 링크 에러가 나는 이유를 ODR 관점에서 설명하세요.

// A.cpp
#include "h.h"

// B.cpp
#include "h.h"

// h.h
extern int x = 10;

그리고 이 문제를 해결하는 세 가지 방법을 제시하세요.


4
inline 변수의 링커 동작을 설명하세요.

  • 왜 여러 TU에 정의가 있어도 에러가 나지 않는가
  • 최종적으로 하나의 주소가 되는 이유

5
다음 코드에서 실제 메모리 인스턴스는 몇 개인가요? 이유는?

// header
inline int x = 0;

// A.cpp
#include "header"

// B.cpp
#include "header"

그리고 이것이 static int x = 0;일 때와 어떻게 다른가요?


6
static 멤버 변수에 대해 설명하세요.

특히 다음 질문에 답하세요:

  1. 왜 클래스 내부 선언만으로 메모리가 생성되지 않는가
  2. 왜 외부 정의가 필요한가
  3. 왜 linkage가 external인가
  4. 왜 instance가 아니라 클래스에 속하는가

7
다음 코드의 호출 형태 차이를 설명하세요.

struct A {
    static void s();
    void f();
};

A a;
a.s();
a.f();

특히:

  • this 전달 여부
  • 호출 규약 차이
  • 심볼 형태 차이

8
const 전역 변수와 constexpr 전역 변수는 왜 기본적으로 internal linkage인가요?
그리고 external로 만들려면 어떻게 해야 하나요?
또한 이 설계가 ODR 측면에서 어떤 장점을 가지는지 설명하세요.


9
다음 중 어떤 것이 링커 심볼에 나타나는가를 고르세요. 이유도 설명하세요.

void f() {
    int a;
    static int b;
}

static int c;
const int d = 10;
inline int e = 10;

10
다음 네 선언의 차이를 설명하세요.

int x;
extern int x;
static int x;
inline int x = 0;
  • 정의/선언 여부
  • linkage
  • ODR 규칙
  • TU 내/외 인스턴스 수
  • 링커 심볼 특성

내용에 대한 질의나, 수정 요청은 저에게 큰 도움이 됩니다.

'C\C++' 카테고리의 다른 글

template  (0) 2026.04.01
Object  (0) 2026.04.01
표현식  (0) 2026.04.01
포인터  (0) 2026.04.01
Virtual  (0) 2026.02.03