컴파일러는 컴파일 타임에 크기를 모르고 어떻게 메모리를 할당합니까?
사용자로부터 정수 입력을 받아 정수 배열의 크기로 사용하는 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.
'programing' 카테고리의 다른 글
Git 별칭 명령에 인수 전달 (0) | 2020.11.15 |
---|---|
vim 인스턴스를 실행하는 다른 tmux 창간에 복사 및 붙여 넣기하는 방법 (0) | 2020.11.15 |
.NET에서 간단한 규칙 엔진 라이브러리 찾기 (0) | 2020.11.15 |
이미지에서 차량 번호판 (ANPR)을 인식하는 방법은 무엇입니까? (0) | 2020.11.15 |
Numpy '스마트'대칭 행렬 (0) | 2020.11.15 |