programing

컴파일러는 컴파일 타임에 크기를 모르고 어떻게 메모리를 할당합니까?

nasanasas 2020. 11. 15. 11:23
반응형

컴파일러는 컴파일 타임에 크기를 모르고 어떻게 메모리를 할당합니까?


사용자로부터 정수 입력을 받아 정수 배열의 크기로 사용하는 C 프로그램을 작성하고 그 값을 사용하여 주어진 크기의 배열을 선언하고 배열의 크기를 확인하여 확인하고 있습니다.

암호:

#include <stdio.h>
int main(int argc, char const *argv[])
{
    int n;
    scanf("%d",&n);
    int k[n];
    printf("%ld",sizeof(k));
    return 0;
}

놀랍게도 정확합니다! 이 프로그램은 필요한 크기의 배열을 만들 수 있습니다.
그러나 모든 정적 메모리 할당은 컴파일 타임에 이루어지며 컴파일 타임에는의 값을 n알 수 없습니다. 그렇다면 컴파일러가 필요한 크기의 메모리를 할당 할 수있는 이유는 무엇입니까?

필요한 메모리를 그렇게 할당 할 수 있다면 malloc()사용하는 동적 할당의 용도는 무엇 calloc()입니까?


이것은 "정적 메모리 할당"이 아닙니다. 배열 k은 가변 길이 배열 (VLA)이며, 이는이 배열의 메모리가 런타임에 할당됨을 의미합니다. 크기는의 런타임 값에 의해 결정됩니다 n.

언어 사양은 특정 할당 메커니즘을 지시하지 않지만 일반적인 구현에서는 k일반적으로 int *런타임에 실제 메모리 블록이 스택에 할당되는 간단한 포인터가됩니다.

VLA sizeof연산자의 경우 런타임에도 평가되므로 실험에서 올바른 값을 얻습니다. 유형 값을 인쇄 하려면 %zu(아님 %ld)을 사용하십시오 size_t.

malloc(및 기타 동적 메모리 할당 함수) 의 주요 목적은 로컬 개체에 적용되는 범위 기반 수명 규칙을 재정의하는 것입니다. 즉로 할당 된 메모리 malloc는 "영원히"할당 된 상태로 유지되거나 명시 적으로 할당을 해제 할 때까지 유지됩니다 free. 로 할당 된 메모리 malloc는 블록 끝에서 자동으로 할당 해제되지 않습니다.

귀하의 예에서와 같이 VLA는 이러한 "범위 파괴"기능을 제공하지 않습니다. 배열은 k여전히 일반 범위 기반 수명 규칙을 따릅니다. 수명은 블록의 끝에서 끝납니다. 이러한 이유로 일반적으로 VLA는 malloc다른 동적 메모리 할당 기능을 대체 할 수 없습니다 .

그러나 "범위를 제거"할 필요가없고 malloc런타임 크기 배열을 할당하는 데 사용할 필요가없는 특정 경우 에 VLA는 실제로 malloc. 다시 말하지만, VLA는 일반적으로 스택에 할당되며 오늘날까지 스택에 많은 양의 메모리를 할당하는 것은 다소 의심스러운 프로그래밍 관행으로 남아 있습니다.


C에서 컴파일러가 VLA (가변 길이 배열)를 지원하는 수단은 컴파일러에 달려 있습니다.를 사용할 필요가 없으며 malloc()때때로 "스택"메모리라고하는 것을 사용할 수 있습니다 (예 : 시스템 사용). 이와 같은 특정 기능 alloca()은 표준 C의 일부가 아닙니다. 스택을 사용하는 경우 malloc()최신 운영 체제에서 프로그램에 스택 메모리 할당량을 훨씬 더 적게 허용하므로 배열의 최대 크기는 일반적으로를 사용하여 가능한 것보다 훨씬 작습니다 .


가변 길이 배열의 메모리는 정적으로 할당 될 수 없습니다. 그러나 스택에 할당 할 수 있습니다. 일반적으로 이것은 스택 포인터에 대한 동적 결정 변경에 직면하여 함수 스택 프레임의 위치를 ​​추적하기 위해 "프레임 포인터"를 사용하는 것을 포함합니다.

프로그램을 컴파일하려고 할 때 실제로 발생하는 것은 가변 길이 배열이 최적화 된 것 같습니다. 그래서 컴파일러가 실제로 배열을 할당하도록 코드를 수정했습니다.

#include <stdio.h>
int main(int argc, char const *argv[])
{
    int n;
    scanf("%d",&n);
    int k[n];
    printf("%s %ld",k,sizeof(k));
    return 0;
}

gcc 6.3을 사용하여 arm을 컴파일하는 Godbolt (arm ASM을 읽을 수 있기 때문에 arm 사용)는 이것을 https://godbolt.org/g/5ZnHfa로 컴파일합니다 . (내 댓글)

main:
        push    {fp, lr}      ; Save fp and lr on the stack
        add     fp, sp, #4    ; Create a "frame pointer" so we know where
                              ; our stack frame is even after applying a 
                              ; dynamic offset to the stack pointer.
        sub     sp, sp, #8    ; allocate 8 bytes on the stack (8 rather
                              ; than 4 due to ABI alignment
                              ; requirements)
        sub     r1, fp, #8    ; load r1 with a pointer to n
        ldr     r0, .L3       ; load pointer to format string for scanf
                              ; into r0
        bl      scanf         ; call scanf (arguments in r0 and r1)
        ldr     r2, [fp, #-8] ; load r2 with value of n
        ldr     r0, .L3+4     ; load pointer to format string for printf
                              ; into r0
        lsl     r2, r2, #2    ; multiply n by 4
        add     r3, r2, #10   ; add 10 to n*4 (not sure why it used 10,
                              ; 7 would seem sufficient)
        bic     r3, r3, #7    ; and clear the low bits so it is a
                              ; multiple of 8 (stack alignment again) 
        sub     sp, sp, r3    ; actually allocate the dynamic array on
                              ; the stack
        mov     r1, sp        ; store a pointer to the dynamic size array
                              ; in r1
        bl      printf        ; call printf (arguments in r0, r1 and r2)
        mov     r0, #0        ; set r0 to 0
        sub     sp, fp, #4    ; use the frame pointer to restore the
                              ; stack pointer
        pop     {fp, lr}      ; restore fp and lr
        bx      lr            ; return to the caller (return value in r0)
.L3:
        .word   .LC0
        .word   .LC1
.LC0:
        .ascii  "%d\000"
.LC1:
        .ascii  "%s %ld\000"

"가변 길이 배열", VLA라고하는이 구조의 메모리는와 유사한 방식으로 스택에 할당됩니다 alloca. 정확히 어떻게 이런 일이 발생하는지는 정확히 어떤 컴파일러를 사용 하느냐에 따라 다르지만 기본적으로 크기를 알고있을 때 크기를 계산 한 다음 스택 포인터에서 전체 크기를 [1] 빼는 경우입니다.

You do need malloc and friends because this allocation "dies" when you leave the function. [And it's not valid in standard C++]

[1] For typical processors that use a stack that "grows towards zero".


When it is said that the compiler allocates memory for variables at compile time, it means that the placement of those variables is decided upon and embedded in the executable code that the compiler generates, not that the compiler is making space for them available while it works. The actual dynamic memory allocation is carried out by the generated program when it runs.

참고URL : https://stackoverflow.com/questions/46387111/how-does-the-compiler-allocate-memory-without-knowing-the-size-at-compile-time

반응형