“컴파일 타임에 할당 된 메모리”는 실제로 무엇을 의미합니까?


159

C 및 C ++와 같은 프로그래밍 언어에서 사람들은 종종 정적 및 동적 메모리 할당을 참조합니다. 나는 개념을 이해하지만 "컴파일 시간 동안 모든 메모리가 할당 (예약)되었다"는 문구는 항상 혼란스러워합니다.

내가 이해하는 것처럼 컴파일은 고급 C / C ++ 코드를 기계 언어로 변환하고 실행 파일을 출력합니다. 컴파일 된 파일에서 메모리는 어떻게 "할당"됩니까? 메모리가 항상 모든 가상 메모리 관리 항목과 함께 RAM에 할당되지 않습니까?

정의에 의한 메모리 할당은 런타임 개념이 아닙니까?

C / C ++ 코드에서 1KB 정적 할당 변수를 만들면 실행 파일의 크기가 같은 양만큼 증가합니까?

이것은 "정적 할당"이라는 제목으로 문구가 사용 된 페이지 중 하나입니다.

기본으로 돌아 가기 : 메모리 할당


코드와 데이터는 대부분의 최신 아키텍처에서 완전히 분리됩니다. 소스 파일은 동일한 위치에 두 코드 데이터를 모두 포함하지만 bin에는 데이터에 대한 참조 만 있습니다. 이는 소스의 정적 데이터가 참조로만 해석됨을 의미합니다.
Cholthi Paul Ttiopic

답변:


184

컴파일 타임에 할당 된 메모리는 프로세스 메모리 맵 내에서 특정 사항이 할당 될 때 컴파일 타임에 컴파일러가 확인하는 것을 의미합니다.

예를 들어, 전역 배열을 고려하십시오.

int array[100];

컴파일러는 컴파일 타임에 배열의 크기와의 크기를 int알고 있으므로 컴파일 타임에 배열의 전체 크기를 알고 있습니다. 또한 전역 변수에는 기본적으로 정적 저장 기간이 있습니다. 이는 프로세스 메모리 공간의 정적 메모리 영역 (.data / .bss 섹션)에 할당됩니다. 이 정보가 주어지면 컴파일러는 컴파일하는 동안 정적 메모리 영역의 주소가 배열로 결정됩니다 .

물론 메모리 주소는 가상 주소입니다. 프로그램은 자체 메모리 공간이 있다고 가정합니다 (예 : 0x00000000에서 0xFFFFFFFF까지). 그렇기 때문에 컴파일러는 "어레이는 주소 0x00A33211에있을 것"과 같은 가정을 할 수 있습니다. 런타임시 해당 주소는 MMU 및 OS에 의해 실제 / 하드웨어 주소로 변환됩니다.

값이 초기화 된 정적 스토리지는 약간 다릅니다. 예를 들면 다음과 같습니다.

int array[] = { 1 , 2 , 3 , 4 };

첫 번째 예에서 컴파일러는 배열이 할당 될 위치 만 결정하고 해당 정보를 실행 파일에 저장했습니다.
값이 초기화 된 것들의 경우 컴파일러는 실행 파일에 배열의 초기 값을 주입하고 프로그램 시작시 배열 할당 후 배열에 이러한 값을 채워야 함을 프로그램 로더에 알려주는 코드를 추가합니다.

다음은 컴파일러가 생성 한 어셈블리의 두 가지 예입니다 (x86 대상이있는 GCC4.8.1).

C ++ 코드 :

int a[4];
int b[] = { 1 , 2 , 3 , 4 };

int main()
{}

출력 어셈블리 :

a:
    .zero   16
b:
    .long   1
    .long   2
    .long   3
    .long   4
main:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $0, %eax
    popq    %rbp
    ret

보시다시피 값은 어셈블리에 직접 주입됩니다. a표준에 따르면 정적 저장 항목은 기본적으로 0으로 초기화되어야하므로 array 에서 컴파일러는 16 바이트의 0 초기화를 생성합니다.

8.5.9 (초기화 기) [참고] :
정적 초기화 기간의 모든 객체는 다른 초기화가 시작되기 전에 프로그램 시작시 0으로 초기화됩니다. 경우에 따라 나중에 추가 초기화가 수행됩니다.

필자는 항상 사람들이 코드를 디스 어셈블하여 컴파일러가 실제로 C ++ 코드로 무엇을하는지 확인하도록 제안합니다. 이것은 스토리지 클래스 / 지속 시간 (이 질문과 같은)에서 고급 컴파일러 최적화에 적용됩니다. 컴파일러에게 어셈블리를 생성하도록 지시 할 수 있지만 인터넷에서이를 친숙한 방식으로 수행 할 수있는 훌륭한 도구가 있습니다. 내가 가장 좋아하는 것은 GCC Explorer 입니다.


2
감사. 이것은 많은 것을 분명히합니다. 따라서 컴파일러는 "변수 배열 [] 등을 위해 0xABC에서 0xXYZ까지 메모리 예약"과 동등한 것을 출력합니다. 로더는 프로그램을 실행하기 직전에 로더를 사용하여 실제로 할당합니다.
Talha 님이

1
@TalhaSayed 정확히. 예를 보려면 편집을 참조하십시오
Manu343726

2
@Secko 나는 일을 단순화했다. 프로그램에 대한 언급은 가상 메모리를 통해 작동하지만 가상 메모리에 관한 질문은 주제를 확장하지 않았습니다. 가상 메모리 덕분에 컴파일러가 컴파일 타임에 메모리 주소에 대한 가정을 할 수 있다고 지적했습니다.
Manu343726

2
@Secko 예. mmm "generated"는 더 나은 용어라고 생각합니다.
Manu343726

2
"프로세스 메모리 공간의 정적 유선 영역에 할당 됨 " 내 프로세스 메모리 공간에서 일부 정적 유선 영역을 할당 한 읽기.
Radiodef

27

컴파일 타임에 할당 된 메모리는 런타임에 추가 할당이 없다는 것을 의미합니다. malloc, new 또는 기타 동적 할당 방법에 대한 호출이 없습니다. 항상 모든 메모리가 필요하지 않더라도 고정 된 양의 메모리 사용량이 있습니다.

정의에 의한 메모리 할당은 런타임 개념이 아닙니까?

메모리는 런타임 이전 에 사용 되지 않지만 실행 시작 직전에 할당이 시스템에 의해 처리됩니다.

C / C ++ 코드에서 1KB 정적 할당 변수를 만들면 실행 파일의 크기가 같은 양만큼 증가합니까?

단순히 정적을 선언해도 실행 파일의 크기가 몇 바이트 이상 증가하지는 않습니다. 초기 값을 유지하기 위해 0이 아닌 초기 값으로 선언하십시오. 오히려 링커는이 로더를 실행하기 직전에 시스템 로더가 생성하는 메모리 요구 사항에이 1KB 양을 추가하기 만하면됩니다.


1
쓰면 static int i[4] = {2 , 3 , 5 ,5 }실행 파일 크기가 16 바이트 증가합니다. "정적으로 선언하면 실행 파일의 크기가 몇 바이트 이상 증가하지 않습니다. 0이 아닌 초기 값으로 선언하면됩니다."초기 값으로 선언하면 의미합니다.
Suraj Jain

실행 파일에는 두 가지 정적 데이터 영역이 있습니다. 하나는 초기화되지 않은 정적 영역과 다른 하나는 초기화 된 정적 영역입니다. 초기화되지 않은 영역은 실제로 크기 표시 일뿐입니다. 프로그램이 실행될 때, 그 크기는 정적 저장 영역을 늘리기 위해 사용되지만 프로그램 자체는 초기화되지 않은 데이터의 양보다 많은 것을 보유하지 않아도됩니다. 초기화 된 스태틱의 경우, 프로그램은 (각) 스태틱의 크기뿐만 아니라 초기화되는 것을 보유해야합니다. 따라서 귀하의 예에서 프로그램에는 2, 3, 5 및 5가 포함됩니다.
mah

배치 위치 / 할당 방법에 대해 정의 된 구현이지만 알아야 할 필요성을 잘 모르겠습니다.
mah

23

컴파일 시간에 할당 된 메모리는 프로그램을로드 할 때 메모리의 일부가 즉시 할당되고이 할당의 크기 및 (상대) 위치는 컴파일시 결정됩니다.

char a[32];
char b;
char c;

이 3 개의 변수는 "컴파일시 할당 됨"이며, 이는 컴파일러가 컴파일시 크기 (고정)를 계산 함을 의미합니다. 변수 a는 메모리의 오프셋이 될 것입니다. 예를 들어 주소 0 b을 가리키면 주소 33과 c34를 가리 킵니다 (정렬 최적화가 필요하지 않음). 따라서 1Kb의 정적 데이터를 할당하면 코드의 크기가 증가하지 않습니다. 코드 내부의 오프셋 만 변경하기 때문입니다. 실제 공간은로드시 할당됩니다 .

커널은이를 추적하고 내부 데이터 구조 (각 프로세스, 페이지 등에 할당 된 메모리 양)를 업데이트해야하므로 실제 메모리 할당은 항상 런타임에 발생합니다. 차이점은 컴파일러는 사용할 각 데이터의 크기를 이미 알고 있으며 프로그램이 실행되는 즉시 할당된다는 것입니다.

또한 상대 주소대해 이야기하고 있음을 기억하십시오 . 변수가있는 실제 주소는 달라집니다. 로드 할 때 커널은 프로세스를 위해 메모리를 확보하고, address x에서 말할 수 있으며, 실행 파일에 포함 된 모든 하드 코딩 된 주소는 x바이트 단위 로 증가 하므로 a예제의 변수 는 address x, b는 address x+33및 곧.


17

스택에 N 바이트를 차지하는 변수를 추가해도 빈의 크기가 N 바이트만큼 증가하지는 않습니다. 실제로 대부분의 경우 몇 바이트 만 추가합니다.
코드에 1000자를 추가하는 방법에 대한 예제부터 시작 하겠습니다. 선형 방식으로 빈의 크기를 늘리십시오.

1k가 천 문자의 문자열 인 경우 다음과 같이 선언됩니다

const char *c_string = "Here goes a thousand chars...999";//implicit \0 at end

그리고 당신은 vim your_compiled_bin실제로, 당신 은 실제로 그 어딘가 빈에서 그 문자열을 볼 수있을 것입니다. 이 경우 예 : 문자열이 완전히 포함되어 있기 때문에 실행 파일이 1k 더 커집니다.
그러나 스택에 ints, chars 또는 longs 의 배열을 할당 하고이 행을 따라 무언가를 루프로 할당하면

int big_arr[1000];
for (int i=0;i<1000;++i) big_arr[i] = some_computation_func(i);

그럼, 아니오 : 쓰레기통을 늘리지 않습니다 ... 물론 전체 그림을 그리지는 않을 것입니다. 쓰레기통에는 실제로 쓰레기통에 필요한 스택의 크기에 대한 정보가 들어 있습니다. 이 정보를 바탕으로 시스템은 스택이라고 불리는 메모리 덩어리를 예약하여 프로그램이 자유 통치권을 얻습니다. 프로세스 (빈 실행 결과)가 시작될 때 스택 메모리는 여전히 시스템에 의해 할당됩니다. 그런 다음 프로세스가 스택 메모리를 관리합니다. 함수 또는 루프 (모든 유형의 블록)가 호출되거나 실행되면 해당 블록에 대한 로컬 변수가 스택으로 푸시되고 제거됩니다 (스택 메모리가 "해제 됨" ). 기능 / 블록. 그래서 선언1000*sizeof(int)
컴파일시 할당에 현재 의미를 이해하게 된 것을 의미합니다 (의견에 따라) : 컴파일 된 bin에는 시스템이 얼마나 많은 메모리를 알기 위해 필요한 정보가 포함 응용 프로그램에 필요한 스택 크기에 대한 정보와 함께 실행될 때 필요한 기능 / 블록 그것이 빈을 실행할 때 시스템이 할당하는 것입니다. 프로그램은 프로세스가됩니다 (음, 빈 실행은 프로세스입니다 ... 음, 내가 말한 것을 얻습니다).
int some_array[100]빈에 몇 바이트의 추가 정보 만 추가하면 시스템에 함수 X에 100*sizeof(int)+ 여유 공간 이 필요하다는 것을 알 수 있습니다 .


고마워 질문 하나 더, 함수에 대한 지역 변수도 컴파일 시간 동안 같은 방식으로 할당됩니까?
Talha 님이

@TalhaSayed : 그렇습니다. "시스템이 어떤 기능 / 블록이 필요한 메모리 양을 알아야하는지에 대한 정보" 라고 말했습니다 . 함수를 호출하는 순간 시스템은 해당 함수에 필요한 메모리를 할당합니다. 함수가 반환되는 순간 해당 메모리가 다시 해제됩니다.
Elias Van Ootegem

C 코드의 주석은 실제로 / 필요하지 않습니다. 예를 들어, 문자열은 컴파일 타임에 한 번만 할당됩니다. 따라서 결코 "해방"되지 않습니다 (또한 용어는 일반적으로 무언가를 동적으로 할당 할 때만 사용된다고 생각합니다) i. i메모리에 상주 했다면 스택에 푸시되어 단어의 의미에서 풀리지 않은 것이지, 그것을 무시 i하거나 c전체 시간 동안 레지스터에 보관됩니다. 물론, 이것은 모두 컴파일러에 달려 있습니다. 즉, 흑백이 아닙니다.
phant0m

@ phant0m : 문자열 에 스택에 할당 되었다고 말한 적이 없으며 포인터 만있을뿐 문자열 자체는 읽기 전용 메모리에 있습니다. 로컬 변수와 관련된 메모리가 free()호출 의 의미에서 해제되지 않는다는 것을 알고 있지만 사용 된 스택 메모리는 나열된 함수가 반환되면 다른 함수에서 사용할 수 있습니다. 코드가 일부와 혼동 될 수 있으므로 코드를 삭제했습니다.
Elias Van Ootegem

아 알 겠어요 이 경우 "나는 당신의 말로 혼란스러워했다"는 의미로 내 의견을 받아들입니다.
phant0m

16

많은 플랫폼에서 각 모듈 내의 모든 전역 또는 정적 할당은 컴파일러에 의해 3 개 이하의 통합 할당 (초기화되지 않은 데이터 (보통 "bss"라고도 함), 초기화 된 쓰기 가능한 데이터 (보통 "데이터"라고 함))으로 통합됩니다. ), 상수 데이터에 대한 하나 ( "const"), 프로그램 내에서 각 유형의 모든 전역 또는 정적 할당은 링커에 의해 각 유형에 대해 하나의 전역으로 통합됩니다. 예를 들어, int4 바이트 라고 가정하면 모듈은 정적 할당으로 다음을 갖습니다.

int a;
const int b[6] = {1,2,3,4,5,6};
char c[200];
const int d = 23;
int e[4] = {1,2,3,4};
int f;

링커는 bss의 경우 208 바이트, "data"의 경우 16 바이트, "const"의 경우 28 바이트가 필요하다는 것을 링커에게 알려줍니다. 또한 변수에 대한 모든 참조는 영역 선택기 및 오프셋으로 대체되므로 a, b, c, d 및 e는 bss + 0, const + 0, bss + 4, const + 24, data로 대체됩니다. +0 또는 bss + 204

프로그램이 연결되면 모든 모듈의 모든 bss 영역이 함께 연결됩니다. 마찬가지로 데이터 및 const 영역. 각 모듈에 대해 bss 관련 변수의 주소는 모든 이전 모듈의 bss 영역의 크기만큼 증가합니다 (데이터 및 const와 마찬가지로). 따라서 링커가 완료되면 모든 프로그램에 하나의 bss 할당, 하나의 데이터 할당 및 하나의 const 할당이 있습니다.

프로그램이로드되면 일반적으로 플랫폼에 따라 다음 4 가지 중 하나가 발생합니다.

  1. 실행 파일은 각 종류의 데이터 및 초기화 된 데이터 영역에 필요한 초기 바이트 수를 나타내는 바이트 수를 나타냅니다. 또한 bss, data 또는 constative 상대 주소를 사용하는 모든 명령어 목록이 포함됩니다. 운영 체제 또는 로더는 각 영역에 적절한 공간을 할당 한 다음 해당 영역의 시작 주소를 필요한 각 명령에 추가합니다.

  2. 운영 체제는 세 종류의 데이터를 모두 보유하기 위해 메모리 청크를 할당하고 애플리케이션에 해당 메모리 청크에 대한 포인터를 제공합니다. 정적 또는 전역 데이터를 사용하는 모든 코드는 해당 포인터를 기준으로이를 역 참조합니다 (대부분의 경우 포인터는 응용 프로그램 수명 동안 레지스터에 저장 됨).

  3. 운영 체제는 이진 코드를 보유하고있는 것을 제외하고는 처음에는 메모리를 응용 프로그램에 할당하지 않지만 응용 프로그램은 운영 체제에 적절한 할당을 요청해야합니다.

  4. 운영 체제는 처음에 응용 프로그램을위한 공간을 할당하지 않지만 시작시 응용 프로그램에 적절한 할당을 요청합니다 (위와 같이). 이 응용 프로그램에는 메모리가 할당 된 위치를 반영하기 위해 업데이트해야하는 주소 목록이 포함 된 명령어 목록이 포함되지만 (첫 번째 스타일과 같이) OS 로더가 응용 프로그램을 패치하는 대신 응용 프로그램 자체에 패치를 적용 할 수있는 충분한 코드가 포함됩니다 .

네 가지 방법 모두 장단점이 있습니다. 그러나 모든 경우에 컴파일러는 임의의 수의 정적 변수를 고정 된 적은 수의 메모리 요청으로 통합하고 링커는 모든 변수를 적은 수의 통합 할당으로 통합합니다. 응용 프로그램이 운영 체제 나 로더에서 메모리 청크를 받아야하지만, 큰 청크에서 개별 조각을 필요한 모든 개별 변수에 할당하는 것은 컴파일러와 링커입니다.


13

귀하의 질문의 핵심은 "컴파일 된 파일에서 메모리가 어떻게"할당 "됩니까? 메모리가 항상 모든 가상 메모리 관리 항목과 함께 RAM에 할당되지 않습니까? 정의에 의한 메모리 할당이 런타임 개념이 아닙니까?"

문제는 메모리 할당과 관련된 두 가지 개념이 있다는 것입니다. 기본적으로 메모리 할당은 "이 데이터 항목은이 특정 메모리 청크에 저장됩니다"라고 말하는 프로세스입니다. 최신 컴퓨터 시스템에서는 두 단계 프로세스가 필요합니다.

  • 일부 시스템은 품목이 저장 될 가상 주소를 결정하는 데 사용됩니다
  • 가상 주소는 실제 주소에 매핑됩니다

후자의 프로세스는 순전히 런타임이지만 데이터의 크기가 알려져 있고 정해진 수의 데이터가 필요한 경우 컴파일 타임에 전자를 수행 할 수 있습니다. 기본적으로 작동 방식은 다음과 같습니다.

  • 컴파일러는 다음과 같은 줄을 포함하는 소스 파일을 봅니다.

    int c;
  • 변수 'c'에 대한 메모리를 예약하도록 지시하는 어셈블러에 대한 출력을 생성합니다. 이것은 다음과 같습니다.

    global _c
    section .bss
    _c: resb 4
  • 어셈블러가 실행될 때 메모리 '세그먼트'(또는 '섹션')의 시작에서 각 항목의 오프셋을 추적하는 카운터를 유지합니다. 이것은 전체 파일의 모든 것을 포함하는 매우 큰 'struct'의 일부와 같습니다. 현재 실제 메모리가 할당되어 있지 않으며 어디든지 될 수 있습니다. _c특정 오프셋 (세그먼트의 시작에서 510 바이트)이 있는 테이블에 기록한 다음 카운터를 4 씩 증가 시키므로 다음 변수는 514 바이트가됩니다. 의 주소가 필요한 코드의 _c경우 출력 파일에 510을 넣고 출력에 _c나중에 추가를 포함 하는 세그먼트의 주소가 필요하다는 메모를 추가합니다.

  • 링커는 모든 어셈블러의 출력 파일을 가져 와서 검사합니다. 각 세그먼트의 주소가 겹치지 않도록 결정하고 명령이 여전히 올바른 데이터 항목을 참조하도록 필요한 오프셋을 추가합니다. 초기화되지 않은 메모리의 경우c(어셈블러는 컴파일러가 초기화되지 않은 메모리에 예약 된 이름 인 '.bss'세그먼트에 메모리를 넣었다는 사실에 의해 메모리가 초기화되지 않는다고 들었습니다), 출력에 운영 체제를 알려주는 헤더 필드를 포함합니다 예약해야 할 금액 재배치 될 수 있지만 일반적으로 특정 메모리 주소에서보다 효율적으로로드되도록 설계되며 OS는이 주소에서로드를 시도합니다. 이 시점에서에 사용될 가상 주소가 무엇인지 잘 알고 있습니다 c.

  • 실제 주소는 프로그램이 실행될 때까지 실제로 결정되지 않습니다. 그러나 프로그래머의 관점에서 실제 주소는 실제로 관련이 없습니다. OS는 일반적으로 누구에게도 말하지 않고 프로그램을 실행하는 동안에도 자주 변경할 수 있기 때문에 실제 주소를 알 수 없습니다. OS의 주요 목적은 어쨌든 이것을 추상화하는 것입니다.


9

실행 파일은 정적 변수에 할당 할 공간을 설명합니다. 이 할당은 실행 파일을 실행할 때 시스템에서 수행됩니다. 따라서 1kB 정적 변수는 1kB로 실행 파일의 크기를 늘리지 않습니다.

static char[1024];

물론 이니셜 라이저를 지정하지 않으면 :

static char[1024] = { 1, 2, 3, 4, ... };

따라서 '기계 언어'(예 : CPU 명령어) 외에도 실행 파일에는 필요한 메모리 레이아웃에 대한 설명이 포함됩니다.


5

메모리는 여러 가지 방법으로 할당 될 수 있습니다.

  • 응용 프로그램 힙 (프로그램이 시작될 때 OS에 의해 앱에 전체 힙이 할당 됨)
  • 운영 체제 힙에서 (더 많은 것을 확보 할 수 있음)
  • 가비지 수집기 제어 힙 (위와 동일)
  • 스택에 (그래서 스택 오버플로를 얻을 수 있음)
  • 바이너리의 코드 / 데이터 세그먼트에 예약 됨 (실행 파일)
  • 원격 위치 (파일, 네트워크-해당 메모리에 대한 포인터가 아닌 핸들을 받음)

이제 귀하의 질문은 "컴파일 타임에 할당 된 메모리"입니다. 확실히 이진 세그먼트 할당 또는 스택 할당 또는 경우에 따라 힙 할당을 나타내는 것으로 잘못 잘못 표현 된 것입니다. 그러나이 경우 할당은 보이지 않는 생성자 호출에 의해 프로그래머의 눈에 숨겨져 있습니다. 또는 아마도 메모리에 힙이 할당되지 않았다고 말하고 싶었지만 스택 또는 세그먼트 할당에 대해 알지 못했을 수도 있습니다.

그러나 대부분의 경우 사람 은 할당되는 메모리의 양이 컴파일 타임에 알려져 있다고 말하기를 원합니다 .

바이너리 크기는 메모리가 앱의 코드 또는 데이터 세그먼트에 예약되어있는 경우에만 변경됩니다.


1
이 답변은 "응용 프로그램 힙", "OS 힙"및 "GC 힙"에 대한 개념이 모두 의미있는 개념 인 것처럼 혼동됩니다. 나는 # 1에 의해 일부 프로그래밍 언어가 (가설 적으로) .data 섹션의 고정 크기 버퍼에서 메모리를 할당하는 "힙 할당"체계를 사용할 수 있다고 말하려고했지만, 비현실적으로 해롭다 고 생각합니다 OP의 이해에. 다시 # 2와 # 3으로, GC의 존재는 실제로 아무것도 바뀌지 않습니다. 그리고 다시 # 5에서 .data와 사이의 비교적 중요한 차이점을 생략했습니다 .bss.
Quuxplusone

4

네 말이 맞아 메모리는 실제로로드시, 즉 실행 파일이 (가상) 메모리로 가져올 때 할당 (페이징)됩니다. 그 순간에 메모리를 초기화 할 수도 있습니다. 컴파일러는 메모리 맵을 만듭니다. [그러므로로드 및 힙 공간도로드 시간에 할당됩니다!]


2

조금 뒤로 물러서야한다고 생각합니다. 컴파일 타임에 할당 된 메모리 .... 무슨 뜻입니까? 아직 설계되지 않은 컴퓨터를 위해 아직 제조되지 않은 칩의 메모리가 어떻게 든 예약되어 있음을 의미 할 수 있습니까? 아니요, 시간 여행, 우주를 조작 할 수있는 컴파일러는 없습니다.

따라서 컴파일러는 런타임에 어떻게 든 메모리를 할당하라는 명령을 생성해야합니다. 그러나 올바른 각도에서 보면 컴파일러는 모든 명령을 생성하므로 차이점은 무엇입니까? 차이점은 컴파일러가 런타임에 코드가 결정을 변경하거나 수정할 수 없다는 점입니다. 컴파일 타임에 50 바이트가 필요하다고 결정하면 런타임에 60을 할당하도록 결정할 수 없습니다. 이는 이미 결정되었습니다.


Socratic 메서드를 사용하는 답변이 마음에 들지만 여전히 "컴파일러가 런타임에 메모리를 할당하는 명령을 생성합니다"라는 잘못된 결론을 내 렸습니다. 컴파일러가 런타임 "명령"을 생성하지 않고 "메모리를 할당"할 수있는 방법을 알아 보려면 가장 투표가 많은 답변을 확인하십시오. 어셈블리 언어 컨텍스트의 "명령"은 실행 가능한 opcode와 같은 특정 의미를 갖습니다. "구두"와 같은 것을 의미하기 위해 구어체로 단어를 사용 했을 수도 있지만이 컨텍스트에서는 OP를 혼동 할 수 있습니다. )
Quuxplusone

1
@ Quuxplusone : 그 대답을 읽었습니다. 그리고 아니요, 제 대답은 초기화 변수의 문제를 구체적으로 다루지 않습니다. 또한 자체 수정 코드는 다루지 않습니다. 그 대답은 훌륭하지만, 내가 중요한 문제라고 생각하는 것을 다루지 않았습니다. 따라서 내 대답은 OP (및 다른 사람들)가 이해하지 못하는 문제가있을 때 무엇이 ​​일어나고 있는지에 대해 생각하고 멈추는 데 도움이되기를 바랍니다.
jmoreno

@Quuxplusone : 여기에 허위 주장을하게된다면 미안하지만, 당신도 내 대답을 -1 한 사람들 중 한 명이라고 생각합니다. 그렇다면 내 대답의 어느 부분이 그렇게하는 주된 원인인지 끔찍하게 지적하고 편집 내용을 확인하고 싶습니까? 스택 메모리 관리 방법의 실제 내부에 대해 몇 가지 부분을 건너 뛰었으므로 이제 내 대답에 100 % 정확하지 않은 것에 대해 조금 추가했습니다. :)
Elias Van Ootegem

@jmoreno 당신이 만든 요점 "아직 설계되지 않은 컴퓨터를 위해 아직 제조되지 않은 칩의 메모리가 어떻게 든 예약되어 있다는 것을 의미 할 수 있습니까?" "할당"이라는 단어가 처음부터 저를 혼란스럽게했던 암시를 의미합니다. 이 답변은 내가 지적하려고했던 문제를 정확하게 나타 내기 때문에 좋아합니다. 여기서 어떤 대답도 그 특정 지점에 실제로 영향을 미치지 않았습니다. 감사.
Talha

2

어셈블리 프로그래밍을 배우면 데이터, 스택 및 코드 등에 대한 세그먼트를 조각해야 함을 알 수 있습니다. 데이터 세그먼트는 문자열과 숫자가있는 곳입니다. 코드 세그먼트는 코드가있는 곳입니다. 이 세그먼트는 실행 프로그램에 내장되어 있습니다. 물론 스택 크기도 중요합니다 ... 스택 오버플로를 원하지 않을 것입니다 !

따라서 데이터 세그먼트가 500 바이트 인 경우 프로그램에는 500 바이트 영역이 있습니다. 데이터 세그먼트를 1500 바이트로 변경하면 프로그램 크기가 1000 바이트 더 커집니다. 데이터는 실제 프로그램으로 조립됩니다.

더 높은 수준의 언어를 컴파일 할 때 이런 일이 일어나고 있습니다. 실제 데이터 영역은 실행 가능 프로그램으로 컴파일 될 때 할당되어 프로그램 크기를 증가시킵니다. 프로그램은 즉시 메모리를 요청할 수 있으며 이는 동적 메모리입니다. RAM에서 메모리를 요청하면 CPU에서 사용하도록 메모리를주고, 사용을 중지 할 수 있으며 가비지 수집기는 메모리를 다시 CPU로 해제합니다. 필요한 경우 우수한 메모리 관리자가 하드 디스크로 교체 할 수도 있습니다. 이러한 기능은 고급 언어가 제공하는 것입니다.


2

몇 가지 다이어그램을 사용하여 이러한 개념을 설명하고 싶습니다.

이것은 컴파일 타임에 메모리를 할당 할 수 없다는 것이 사실입니다. 그러나 컴파일 타임에 실제로 어떤 일이 발생합니까?

여기에 설명이 있습니다. 예를 들어 프로그램에 4 개의 변수 x, y, z 및 k가 있다고 가정하십시오. 이제 컴파일 타임에 단순히 메모리 맵을 작성하여 서로에 대한 이러한 변수의 위치가 확인됩니다. 이 다이어그램이 더 잘 설명 할 것입니다.

이제 메모리에서 프로그램이 실행되고 있지 않다고 상상해보십시오. 이것은 큰 빈 사각형으로 표시됩니다.

빈 필드

다음으로이 프로그램의 첫 번째 인스턴스가 실행됩니다. 다음과 같이 시각화 할 수 있습니다. 실제로 메모리가 할당되는 시간입니다.

첫 번째 사례

이 프로그램의 두 번째 인스턴스가 실행 중이면 메모리는 다음과 같습니다.

두 번째 사례

그리고 세 번째 ..

세 번째 사례

등등.

이 시각화가이 개념을 잘 설명하기를 바랍니다.


2
이러한 다이어그램이 정적 메모리와 동적 메모리의 차이를 보여 주면 IMHO가 더 유용 할 것입니다.
Bartek Banachewicz

이것은 일을 단순하게 유지하기 위해 의도적으로 피했습니다. 저의 초점은 기술적 인 혼란없이 명확하게이 기초를 설명하는 것입니다. 정적 변수.
user3258051 2019

1
Eh,이 개념은 특별히 복잡하지 않기 때문에 왜 그것이 필요한 것보다 단순하게 만드는지 알지 못하지만 무료 답변으로 만 사용됩니다.
Bartek Banachewicz

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.