변수는 어떻게 프로그램 스택에 저장되고 검색됩니까?


47

이 질문의 순진함에 대해 미리 사과드립니다. 저는 컴퓨터를 처음으로 제대로 이해하려고 노력하는 50 세의 예술가입니다. 여기로갑니다.

컴파일러가 데이터 유형과 변수를 처리하는 방법을 이해하려고 노력했습니다 (매우 일반적인 의미에서는 많은 것이 있음을 알고 있습니다). "스택"의 스토리지와 값 유형 간의 관계와 "힙"의 스토리지 및 참조 유형 간의 관계에 대한 이해에서 뭔가 빠진 것이 있습니다 (인용 부호는 이러한 용어가 추상화가 아니라는 것을 이해하기 위해 의미합니다) 내가이 질문을 짜는 방법과 같은 단순화 된 맥락에서 문자 그대로 너무 취해지기를 바랍니다). 어쨌든, 간단한 아이디어는 부울 및 정수와 같은 유형은 저장 공간 측면에서 알려진 엔티티이므로 범위가 쉽게 제어되므로 "스택"으로 이동한다는 것입니다.

그러나 내가 얻지 못하는 것은 응용 프로그램에서 스택의 변수를 읽는 방법입니다. 정의 x하고 정수로 할당 하면 x = 3스토리지가 스택에 예약 된 다음 그 값 3이 저장됩니다. 내가 선언하고 할당 동일한 기능 y말,로 4하고 그때 사용하는 것이 다음의 x또 다른 표현으로는 (예를 들어 z = 5 + x프로그램이 읽을 수있는 방법을) x위해 평가하기 z는 아래의 경우y스택에? 나는 분명히 무언가를 놓치고있다. 스택의 위치는 변수의 수명 / 범위에 불과하며 전체 스택은 실제로 프로그램에 항상 액세스 할 수 있습니까? 그렇다면 값을 검색 할 수 있도록 스택에 변수의 주소 만 보유하는 다른 인덱스가 있음을 의미합니까? 그러나 스택의 요점은 변수 주소와 같은 위치에 값이 저장되었다는 것입니다. 내 겁나는 마음 으로이 다른 색인이 있다면 우리는 힙과 같은 것에 대해 이야기하고있는 것 같습니다. 나는 매우 혼란스러워하고 단순한 질문에 대한 간단한 대답이 있기를 바라고 있습니다.

읽어 주셔서 감사합니다.


7
@ fade2black 동의하지 않습니다-중요한 요점을 요약 한 합리적인 길이의 답변을 제공 할 수 있어야합니다.
David Richerby 2016 년

9
당신은 종류를 저장 하는 위치 와 혼동시키는 매우 일반적인 오류를 만들고 있습니다. bools가 스택에 있다고 말하는 것은 단순히 거짓입니다. 부울은 변수에 들어가고 , 수명이 짧으면 변수에 쌓이고 , 수명이 짧지 않으면 힙에 입니다. 이 C #을 어떻게 관련되는지에 대한 몇 가지 생각을 참조 blogs.msdn.microsoft.com/ericlippert/2010/09/30/...
에릭 Lippert의

7
또한 스택을 변수 스택으로 생각하지 마십시오 . 이를 메소드활성화 프레임 스택으로 생각하십시오 . 메소드 내에서 해당 메소드의 활성화 변수에 액세스 할 수 있지만 호출자의 변수 는 스택 맨 위에있는 프레임에 없으므로 호출자의 변수에 액세스 할 수 없습니다 .
Eric Lippert

5
또한 : 새로운 것을 배우고 언어의 구현 세부 사항을 파고 드는 이니셔티브를 취한 것에 대해 박수를 보냅니다. 여기 흥미로운 흥미로운 걸림돌이 있습니다 : 추상 데이터 형식 의 스택이 무엇인지 이해 하지만 활성화 및 연속을 구체화하기위한 구현 세부 사항은 아닙니다 . 후자는 스택 추상 데이터 유형의 규칙을 따르지 않습니다 . 규칙보다 지침으로 취급합니다. 프로그래밍 언어의 핵심은 프로그래밍 문제를 해결하기 위해 추상화 된 세부 사항을 이해할 필요가 없다는 것입니다.
Eric Lippert

4
Eric, Sava, Thumbnail에게 감사합니다. 이러한 의견과 참조는 모두 매우 유용합니다. 나는 당신이 같은 사람들이 내 것과 같은 질문을 볼 때 내면으로 신음해야한다고 생각하지만 답변을 얻는 것에 대한 엄청난 흥분과 만족을 알고하십시오!
Celine Atwood

답변:


24

스택에 로컬 변수를 저장하는 것은 구현 세부 사항 – 기본적으로 최적화입니다. 이런 식으로 생각할 수 있습니다. 함수를 입력하면 모든 지역 변수에 대한 공간이 어딘가에 할당됩니다. 그런 다음 변수 위치를 알고 있기 때문에 모든 변수에 액세스 할 수 있습니다 (이는 할당 프로세스의 일부 임). 함수를 떠날 때 공간이 할당 해제됩니다 (해제).

스택은이 프로세스를 구현하는 한 가지 방법입니다. 크기가 제한적인 작은 변수에만 적합한 일종의 "빠른 힙"으로 생각할 수 있습니다. 추가 최적화로 모든 지역 변수는 하나의 블록에 저장됩니다. 각 지역 변수의 크기는 알려져 있으므로 블록에있는 각 변수의 오프셋을 알고 있으므로 액세스하는 방식입니다. 이것은 주소가 다른 변수에 저장된 힙에 할당 된 변수와 대조적입니다.

스택을 기존 스택 데이터 구조와 매우 유사하다고 생각할 수 있지만 한 가지 중요한 차이점이 있습니다. 스택 상단 아래의 항목에 액세스 할 수 있습니다. 실제로, 상단에서 번째 항목에 액세스 할 수 있습니다 . 이것은 당신이 밀고 터지는 방법으로 모든 지역 변수에 접근 할 수있는 방법입니다. 유일한 추진은 기능에 들어가는 것이고, 기능을 떠날 때만 터지는 것입니다.케이

마지막으로, 실제로는 로컬 변수 중 일부가 레지스터에 저장되어 있다고 언급하겠습니다. 레지스터에 대한 액세스가 스택에 대한 액세스보다 빠르기 때문입니다. 이것은 지역 변수를위한 공간을 구현하는 또 다른 방법입니다. 다시 한번, 변수가 어디에 저장되어 있는지 (이번에는 오프셋이 아니라 레지스터 이름을 통해) 정확히 알고 있으며, 이런 종류의 저장은 작은 데이터에만 적합합니다.


1
"한 블록에 할당"은 다른 구현 세부 사항입니다. 그러나 중요하지 않습니다. 컴파일러는 로컬 변수에 메모리가 어떻게 필요한지 알고 해당 메모리에 하나 이상의 블록을 할당 한 다음 해당 메모리에 로컬 변수를 만듭니다.
MSalters 2016 년

고마워요 실제로 이러한 "블록"중 일부는 레지스터 일뿐입니다.
Yuval Filmus 2016 년

1
반환 주소를 저장하려면 스택 만 있으면됩니다. 힙의 리턴 주소에 포인터를 전달하여 스택없이 재귀를 매우 쉽게 구현할 수 있습니다.
Yuval Filmus 2016 년

1
@MikeCaron Stacks는 재귀와 거의 관련이 없습니다. 다른 구현 전략에서 왜 "변수를 날려 버리겠습니까?"
gardenhead

1
@gardenhead 가장 명백한 대안 (및 실제로 사용 된 대안)은 각 프로 시저의 변수를 정적으로 할당하는 것입니다. 빠르고 간단하며 예측 가능하지만 재귀 또는 재진입은 허용되지 않습니다. 그것과 기존의 스택은 물론 대안 이 될 수있는 유일한 대안 은 아니지만 (동적으로 모든 것을 할당하는 것은 또 다른 것입니다), 그들은 일반적으로 스택을 정당화 할 때 논의 할 것입니다 :)
hobbs

23

갖는 y스택에 물리적으로 방해하지 않는 x당신이 지적했듯이, 컴퓨터가 다른 스택에서 다른 스택하게하는, 액세스되는.

프로그램이 컴파일 될 때 스택의 변수 위치도 미리 결정됩니다 (함수의 컨텍스트 내에서). 당신의 예에서, 스택이 포함 된 경우 xy그 다음 프로그램이 알고있다 "위에" 사전에x1 개 항목 스택의 맨 아래에있을 것입니다 함수 내부에있는 동안. 컴퓨터 하드웨어는 스택 맨 아래에 1 개의 항목을 명시 적으로 요청할 수 있으므로 컴퓨터 는 존재 x하더라도 얻을 수 y있습니다.

스택의 위치는 변수의 수명 / 범위에 불과하며 전체 스택은 실제로 프로그램에 항상 액세스 할 수 있습니까?

예. 함수를 종료하면 스택 포인터가 이전 위치로 되돌아 가서 효과적으로 삭제 x되고 y, 그러나 기술적으로는 메모리가 다른 용도로 사용될 때까지 계속 유지됩니다. 또한, 함수가 다른 함수를 호출하는 경우 xy여전히있을 것 의도적으로 너무 멀리 아래로 스택 이동하여 액세스 할 수 있습니다.


1
이것은 OP가 테이블에 가져 오는 배경 지식을 넘어서 이야기하지 않는 측면에서 지금까지 가장 깨끗한 대답처럼 보입니다. 실제로 OP를 타겟팅하면 +1!
벤 I.

1
동의합니다! 모든 답변이 매우 도움이되고 매우 감사하지만이 전체 스택 / 힙이 가치 / 참조 유형 구분이 어떻게 발생하는지 이해하는 데 절대적으로 중요하다는 것을 느끼기 때문에 원래 게시물이 동기 부여되었습니다. ' "스택"의 상단 만 볼 수 있다면 어떻습니까? 그래서 당신의 대답은 저에게서 해방됩니다. 물리학의 다양한 역 제곱 법칙을 구체에서 나오는 방사선의 기하학에서 단순히 떨어 뜨리고 간단한 다이어그램을 그려 볼 수 있다는 사실을 처음 알게되었을 때와 같은 느낌이 들었습니다.
Celine Atwood

더 높은 수준 (예를 들어, 언어)에서 어떤 현상이 어떻게 추상화 트리 아래로 조금 더 기본적인 현상으로 인해 실제로 어떻게 그리고 왜인지 알 수있을 때 항상 도움이되기 때문에 나는 그것을 좋아합니다. 아주 단순하게 유지하더라도.
Celine Atwood

1
@CelineAtwood 스택에서 변수를 제거한 후 "강제로"변수에 액세스하려고하면 예기치 않은 / 정의되지 않은 동작이 발생하므로 수행해서는 안됩니다. 일부 언어에서는 시도 할 수없는 "b / c"라고 말하지 않았습니다. 여전히 프로그래밍 실수 일 수 있으므로 피해야합니다.
code_dredd

12

컴파일러가 스택을 관리하는 방법과 스택의 값에 액세스하는 방법에 대한 구체적인 예를 제공하기 위해 시각적 GCC아키텍처와 대상 아키텍처가 i386 인 Linux 환경에서 생성 된 코드를 볼 수 있습니다 .

1. 스택 프레임

아시다시피 스택은 함수 또는 프로 시저 에서 사용하는 실행중인 프로세스의 주소 공간에있는 위치입니다. 즉, 공간이 로컬에 선언 된 변수 및 함수에 전달 된 인수에 대해 스택에 할당된다는 의미입니다 ( 함수 외부에서 선언 된 변수 (예 : 전역 변수)를위한 공간은 가상 메모리의 다른 영역에 할당됩니다. 함수의 모든 데이터에 할당 된 공간을 스택 프레임 이라고합니다 . 다음은 여러 스택 프레임을 시각적으로 묘사 한 것입니다 ( 컴퓨터 시스템 : 프로그래머의 관점 ).

CSAPP 스택 프레임

2. 스택 프레임 관리 및 가변 위치

특정 스택 프레임 내에서 스택에 기록 된 값이 컴파일러에 의해 관리되고 프로그램에 의해 읽히려면 이러한 값의 위치를 ​​계산하고 메모리 주소를 검색하는 방법이 있어야합니다. 스택 포인터라고하는 CPU의 레지스터와 기본 포인터가이를 도와줍니다.

기본적으로 기본 포인터 ebp에는 스택의 하단 또는 기본 메모리 주소가 포함됩니다. 스택 프레임 내 모든 값의 위치는 기준 포인터의 주소를 참조로 사용하여 계산할 수 있습니다. 이것은 위 그림에 묘사되어 %ebp + 4있습니다. 예를 들어베이스 포인터에 4를 더한 메모리 주소가 저장되어 있습니다.

3. 컴파일러 생성 코드

그러나 내가 얻지 못하는 것은 응용 프로그램에서 스택의 변수를 읽는 방법입니다 .x를 정수로 선언하고 x = 3과 같이 할당하고 스토리지가 스택에 예약 된 다음 3의 값이 저장되는 경우 같은 함수에서 y를 선언하고 4와 같이 할당 한 다음 x를 다른 식 (예 : z = 5 + x)에 사용하여 프로그램이 z를 평가하기 위해 어떻게 x를 읽을 수 있습니까? 스택에서 y 아래입니까?

이것이 어떻게 작동하는지보기 위해 C로 작성된 간단한 예제 프로그램을 사용하자 :

int main(void)
{
        int x = 3;
        int y = 4;
        int z = 5 + x;

        return 0;
}

이 C 소스 텍스트에 대해 GCC에서 생성 한 어셈블리 텍스트를 살펴 보겠습니다 (명확성을 위해 약간 정리했습니다).

main:
    pushl   %ebp              # save previous frame's base address on stack
    movl    %esp, %ebp        # use current address of stack pointer as new frame base address
    subl    $16, %esp         # allocate 16 bytes of space on stack for function data
    movl    $3, -12(%ebp)     # variable x at address %ebp - 12
    movl    $4, -8(%ebp)      # variable y at address %ebp - 8
    movl    -12(%ebp), %eax   # write x to register %eax
    addl    $5, %eax          # x + 5 = 9
    movl    %eax, -4(%ebp)    # write 9 to address %ebp - 4 - this is z
    movl    $0, %eax
    leave

우리가 관찰하는 변수는 X, Y 및 Z는 어드레스에 위치된다는 점이다 %ebp - 12, %ebp -8그리고 %ebp - 4각각. 즉, 스택 프레임 내의 변수 위치 main()는 CPU 레지스터에 저장된 메모리 주소를 사용하여 계산됩니다 %ebp.

4. 스택 포인터 이외의 메모리에있는 데이터가 범위를 벗어남

나는 분명히 무언가를 놓치고있다. 스택의 위치는 변수의 수명 / 범위에 불과하며 전체 스택은 실제로 프로그램에 항상 액세스 할 수 있습니까? 그렇다면 값을 검색 할 수 있도록 스택에 변수의 주소 만 보유하는 다른 인덱스가 있음을 의미합니까? 그러나 스택의 요점은 변수 주소와 같은 위치에 값이 저장되었다는 것입니다.

스택은 가상 메모리의 영역으로, 컴파일러에서 사용을 관리합니다. 컴파일러는 스택 포인터 이상의 값 (스택 상단의 값)이 절대 참조되지 않는 방식으로 코드를 생성합니다. 함수가 호출 될 때 스택 포인터의 위치가 변경되어 스택에서 "범위를 벗어난"것으로 간주되는 공간을 생성합니다.

함수가 호출되고 리턴되면 스택 포인터가 감소하고 증가합니다. 이 범위를 벗어난 후 스택에 기록 된 데이터는 사라지지 않지만, 컴파일러는 사용하여 이러한 데이터의 주소를 계산할 수있는 방법이 없기 때문에 컴파일러는이 데이터를 참조하는 지침을 생성하지 않습니다 %ebp%esp.

5. 요약

CPU에 의해 직접 실행될 수있는 코드는 컴파일러에 의해 생성됩니다. 컴파일러는 스택, 함수의 스택 프레임 및 CPU 레지스터를 관리합니다. i386 아키텍처에서 실행되도록 코드에서 스택 프레임의 변수 위치를 추적하기 위해 GCC에서 사용하는 전략 중 하나는 스택 프레임 기본 포인터의 메모리 주소를 %ebp참조로 사용하고 변수의 값을 스택 프레임의 위치에 쓰는 것입니다. 에서의 주소로 오프셋합니다 %ebp.


그 이미지가 어디에서 왔는지 물어봐도 될까요? 그것은 의심 할 여지없이 친숙해 보인다 ... :-) 과거의 교과서에 있었을 것이다.
그레이트 덕

1
nvmd. 방금 링크를 보았습니다. 내가 생각한 것입니다. 해당 책을 공유 한 +1
그레이트 덕

1
gcc 어셈블리 데모의 경우 +1 :)
flow2k

9

ESP (스택 포인터)와 EBP (기본 포인터)의 두 가지 특수 레지스터가 있습니다. 프로 시저가 호출 될 때 처음 두 작업은 일반적으로

push        ebp  
mov         ebp,esp 

첫 번째 작업은 스택에 EBP 값을 저장하고 두 번째 작업은 스택 포인터 값을 기본 포인터에로드하여 로컬 변수에 액세스합니다. 따라서 EBP는 ESP와 동일한 위치를 가리 킵니다.

어셈블러는 변수 이름을 EBP 오프셋으로 변환합니다. 예를 들어 두 개의 로컬 변수 x,y가 있고 다음과 같은 것이 있다면

  x = 1;
  y = 2;
  return x + y;

다음과 같이 번역 될 수 있습니다.

   push        ebp  
   mov         ebp,esp
   mov  DWORD PTR [ ebp + 6],  1   ;x = 1
   mov  DWORD PTR [ ebp + 14], 2   ;y = 2
   mov  eax, [ ebp + 6 ]
   add  [ ebp + 14 ], eax          ; x + y 
   mov  eax, [ ebp + 14 ] 
   ...  

오프셋 값 6과 14는 컴파일 타임에 계산됩니다.

이것은 대략 작동 방식입니다. 자세한 내용은 컴파일러 설명서를 참조하십시오.


14
이것은 인텔 x86에만 해당됩니다. ARM에서는 레지스터 SP (R13)와 FP (R11)가 사용됩니다. x86에서 레지스터가 없다는 것은 공격적인 컴파일러가 ESP에서 파생 될 수 있으므로 EBP를 사용 하지 않음을 의미합니다 . 마지막 예에서는 모든 EBP 기준 주소 지정을 다른 변경없이 ESP 기준으로 변환 할 수 있습니다.
MSalters 2016 년

x, y를위한 공간을 만들기 위해 ESP에 SUB가 없습니까?
Hagen von Eitzen 2016 년

아마 @HagenvonEitzen. 하드웨어 레지스터를 사용하여 스택에 할당 된 변수에 액세스하는 방법에 대한 아이디어를 표현하고 싶었습니다.
fade2black

Downvoters, 의견 부탁드립니다 !!!
fade2black

8

stack에 저장된 로컬 변수가 stack의 액세스 규칙 인 First In Last Out 또는 FILO로 액세스되지 않기 때문에 혼동됩니다 .

문제는 FILO 규칙 이 로컬 변수가 아닌 함수 호출 시퀀스 및 스택 프레임 에 적용된다는 것입니다.

스택 프레임이란 무엇입니까?

함수를 입력하면 스택에 스택 메모리라고하는 메모리가 주어진다. 함수의 지역 변수는 스택 프레임에 저장됩니다. 스택 프레임의 크기는 함수마다 지역 변수의 수와 크기가 다르기 때문에 함수마다 다릅니다.

로컬 변수가 스택 프레임에 저장되는 방법은 FILO와 관련이 없습니다. (소스 코드에 지역 변수가 나타나는 순서조차도 지역 변수가 그 순서대로 저장되는 것을 보장하지는 않습니다.) 질문에서 올바르게 추론 한 것처럼, "변수의 주소 만 보유하는 다른 인덱스가 있습니다 스택에서 값을 검색 할 수 있습니다. " 로컬 변수의 주소는 일반적으로 스택 프레임의 경계 주소와 같은 기본 주소 및 각 로컬 변수에 특정한 오프셋 값 으로 계산됩니다 .

이 FILO 동작은 언제 나타 납니까?

이제 다른 함수를 호출하면 어떻게됩니까? 피 호출자 함수에는 자체 스택 프레임이 있어야하며이 스택 프레임 이 스택에 푸시 됩니다 . 즉, 수신자 함수의 스택 프레임은 호출자 함수의 스택 프레임 위에 배치됩니다. 그리고이 피 호출자 함수가 다른 함수를 호출하면 스택 프레임이 스택의 맨 위에 다시 푸시됩니다.

함수가 반환되면 어떻게됩니까? 수신자 함수가 호출자 함수로 돌아 오면 수신자 함수의 스택 프레임이 스택에서 튀어 나와 나중에 사용할 수있는 공간이 생깁니다.

그래서 당신의 질문에서 :

스택의 위치는 변수의 수명 / 범위에 불과하며 전체 스택은 실제로 프로그램에 항상 액세스 할 수 있습니까?

스택 프레임의 로컬 변수 값이 함수가 반환 될 때 실제로 지워지지 않기 때문에 여기서는 정확합니다. 값이 저장된 메모리 위치는 함수의 스택 프레임에 속하지 않지만 값은 그대로 유지됩니다. 다른 함수가 위치를 포함하는 스택 프레임을 얻고 다른 값을 해당 메모리 위치에 쓰는 경우 값이 지워집니다.

그렇다면 스택과 스택의 차이점은 무엇입니까?

스택과 힙은 메모리의 일부 공간을 나타내는 이름이라는 점에서 동일합니다. 주소를 사용하여 메모리의 모든 위치에 액세스 할 수 있으므로 스택 또는 힙의 모든 위치에 액세스 할 수 있습니다.

차이점은 컴퓨터 시스템이 컴퓨터를 어떻게 사용할 지에 대한 약속에서 비롯됩니다. 말했듯이 힙은 참조 유형입니다. 힙의 값은 특정 스택 프레임과 관련이 없으므로 값의 범위는 어떤 함수와도 관련이 없습니다. 그러나 로컬 변수는 함수 내에서 범위가 지정 되며 현재 함수의 스택 프레임 외부에있는 로컬 변수 값에 액세스 할 있지만 시스템은 다음을 사용하여 이러한 종류의 동작이 발생하지 않도록합니다. 스택 프레임. 이것은 지역 변수가 특정 함수의 범위에 속한다는 일종의 환상을 제공합니다.


4

언어 런타임 시스템으로 로컬 변수를 구현하는 방법에는 여러 가지가 있습니다. 스택을 사용하는 것은 많은 실제 경우에 사용되는 일반적인 효율적인 솔루션입니다.

직관적으로 스택 포인터 sp는 런타임에 고정 주소 또는 레지스터로 유지됩니다. 실제로 중요합니다. 모든 "푸시"가 스택 포인터를 증가 시킨다고 가정하십시오.

로 컴파일시에, 컴파일러는 각 변수의 어드레스를 결정하는 sp - K곳은 K단 변수 (따라서, 컴파일 시간에 계산 될 수있다)의 범위에 의존하는 상수이다.

여기서는 "스택"이라는 단어를 느슨한 의미로 사용하고 있습니다. 이 스택은 push / pop / top 작업을 통해서만 액세스 할뿐만 아니라을 사용하여 액세스 할 수도 있습니다 sp - K.

예를 들어 다음 의사 코드를 고려하십시오.

procedure f(int x, int y) {
  print(x,y);    // (1)
  if (...) {
    int z=x+y; // (2)
    print(x,y,z);  // (3)
  }
  print(x,y); // (4)
  return;
}

프로 시저가 호출되면 x,y스택에서 인수 를 전달할 수 있습니다. 간단히하기 위해 호출자가 x먼저 푸시 한 다음 규칙을 가정합니다 y.

그런 다음 (1) 지점의 컴파일러는 xat sp - 2yat을 찾을 수 있습니다 sp - 1.

포인트 (2)에서, 새로운 변수가 범위 내에 들어온다. 컴파일러는를 나타내는 코드 x+y, 즉 sp - 2and 가 가리키는 코드를 생성 sp - 1하고 스택의 합계 결과를 푸시합니다.

지점 (3)에서 z인쇄됩니다. 컴파일러는 해당 변수가 범위의 마지막 변수임을 알고 있으므로로 지적합니다 sp - 1. 이것은 더 이상 없습니다 y때문에 sp변경되었습니다. 그래도 y컴파일러 를 인쇄 하려면이 범위에서 컴파일러를 찾을 수 있음을 알고 있습니다 sp - 2. 마찬가지로 x현재에서 찾을 수 있습니다 sp - 3.

지점 (4)에서 범위를 종료합니다. z튀어 나와y 다시 주소에서 발견 sp - 1하고, x에 있습니다 sp - 2.

우리가 돌아 오면 f 호출자가 x,y스택에서 튀어 나옵니다 .

따라서 K컴파일러의 컴퓨팅 은 대략 범위 내에있는 변수의 수를 세는 것입니다. 실제로는 모든 변수의 크기가 같지 않기 때문에 실제로는 더 복잡합니다.K 이 약간 더 복잡합니다. 때때로, 스택도의 반환 주소를 포함하고 f있으므로, K"건너 뛰기"그뿐만 아니라해야합니다. 그러나 이것들은 기술입니다.

일부 프로그래밍 언어에서는 더 복잡한 기능을 처리해야 할 경우 상황이 더욱 복잡해질 수 있습니다. 예를 들어 K, 중첩 프로 시저가 재귀적일 경우 많은 반환 주소를 "스킵" 해야하므로 중첩 프로 시저에는 매우 신중한 분석이 필요합니다 . 클로저 / 람다 / 익명 함수도 "캡처 된"변수를 처리하기 위해 약간의주의가 필요합니다. 여전히 위의 예는 기본 아이디어를 설명해야합니다.


3

가장 쉬운 아이디어는 변수를 메모리의 주소대한 수정 이름 으로 생각하는 것입니다 . 실제로 일부 어셈블러는 그런 식으로 기계어 코드를 표시합니다 ( "주소에 값 5 저장"i ", 여기서 i변수 이름 임).

이러한 주소 중 일부는 전역 변수와 같이 "절대"이고 일부는 로컬 변수와 같이 "상대적"입니다. 함수의 변수 (즉, 주소)는 모든 함수 호출마다 다른 "스택"의 특정 위치에 상대적입니다. 그런 식으로 같은 이름이 다른 실제 객체를 참조 할 수 있으며 같은 함수에 대한 순환 호출은 독립 메모리에서 작동하는 독립 호출입니다.


2

스택에 들어갈 수있는 데이터 항목은 스택에 놓입니다. 프리미엄 공간입니다. 또한 일단 x스택에 넣은 다음 스택에 밀어 y넣으면 이상적으로는 액세스 x할 수 없습니다 y. y에 액세스 하려면 팝 을 해야합니다 x. 당신은 그들에게 맞습니다.

스택은 변수가 아니라 frames

당신이 잘못한 곳은 스택 자체에 관한 것입니다. 스택에서 직접 푸시되는 데이터 항목이 아닙니다. 오히려, 스택에 뭔가 stack-frame가 밀려납니다. 이 스택 프레임 에는 데이터 항목이 포함 됩니다. 스택 내에서 깊은 프레임에 액세스 할 수 없지만 최상위 프레임 및 그 안에 포함 된 모든 데이터 항목에 액세스 할 수 있습니다.

데이터 항목이 두 개의 스택 프레임 frame-x과에 묶여 있다고 가정 해 보겠습니다 frame-y. 우리는 그들을 차례로 밀었다. 이제 frame-y위에있는 한 frame-x내부의 데이터 항목에 이상적으로 액세스 할 수 없습니다 frame-x. frame-y보이는 것만 . 그러나 주어진frame-y 표시되는 경우 번들로 제공되는 모든 데이터 항목에 액세스 할 수 있습니다. 프레임 전체가 표시되어 그 안에 포함 된 모든 데이터 항목이 노출됩니다.

답변 끝. 이 프레임에 더 많은 (랜트)

컴파일하는 동안 프로그램의 모든 기능 목록이 작성됩니다. 그런 다음 각 기능에 대해 스택 가능한 데이터 항목 목록 이 작성됩니다. 그런 다음 각 기능에 대해 a stack-frame-template가 만들어집니다. 이 템플릿은 선택된 모든 변수, 함수의 입력 데이터를위한 공간, 출력 데이터 등을 포함하는 데이터 구조입니다. 이제 런타임 동안 함수가 호출 될 때마다이 복사본이 template모든 입력 및 중간 변수와 함께 스택에 저장됩니다. . 이 함수가 다른 함수를 호출하면 해당 함수의 새로운 복사본이 stack-frame스택에 저장됩니다. 지금만큼 해당 기능이 실행되고, 기능의 데이터 항목이 유지됩니다. 일단 기능 단부의 스택 프레임 튀어된다. 지금 스택 프레임이 활성화되어 있으며이 함수는 모든 변수에 액세스 할 수 있습니다.

스택 프레임의 구조와 구성은 프로그래밍 언어에 따라 다릅니다. 언어 내에서도 구현마다 약간의 차이가있을 수 있습니다.


CS를 고려해 주셔서 감사합니다. 나는 피아노 레슨을받는 지금 프로그래머입니다. :)

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