[memory-management] 스택과 힙은 무엇과 어디에 있습니까?

프로그래밍 언어 서적은 값 유형이 스택에 작성 되고 참조 유형이 힙에 작성 되며이 두 가지가 무엇인지 설명하지 않습니다. 나는 이것에 대한 명확한 설명을 읽지 못했습니다. 스택 이 무엇인지 이해합니다 . 그러나,

  • 그것들은 어디에 그리고 무엇입니까 (실제 컴퓨터의 메모리에서)?
  • OS 또는 언어 런타임에 의해 어느 정도까지 제어됩니까?
  • 그들의 범위는 무엇입니까?
  • 각각의 크기는 어떻게 결정됩니까?
  • 무엇이 더 빨라 집니까?


답변

스택은 실행 스레드를위한 스크래치 공간으로 남겨진 메모리입니다. 함수가 호출되면 블록은 로컬 변수 및 일부 부기 데이터를 위해 스택 맨 위에 예약됩니다. 해당 함수가 반환되면 블록이 사용되지 않고 다음에 함수를 호출 할 때 사용할 수 있습니다. 스택은 항상 LIFO (last in first out) 순서로 예약됩니다. 가장 최근에 예약 된 블록은 항상 해제 될 다음 블록입니다. 이렇게하면 스택을 추적하는 것이 정말 간단합니다. 스택에서 블록을 해제하는 것은 하나의 포인터를 조정하는 것입니다.

힙은 동적 할당을위한 메모리 세트입니다. 스택과 달리, 힙에서 블록을 할당하고 할당을 해제하는 데 적용되는 패턴은 없습니다. 언제든지 블록을 할당하고 언제든지 해제 할 수 있습니다. 이것은 주어진 시간에 힙의 어느 부분이 할당되거나 해제되는지 추적하는 것이 훨씬 더 복잡합니다. 다양한 사용 패턴에 대한 힙 성능을 조정하는 데 사용할 수있는 많은 사용자 지정 힙 할당자가 있습니다.

각 스레드는 스택을 가져 오지만 일반적으로 응용 프로그램에 대해 하나의 힙만 있습니다 (다양한 유형의 할당에 대해 여러 힙을 갖는 경우는 드물지만).

질문에 직접 대답하려면 :

OS 또는 언어 런타임에 의해 어느 정도까지 제어됩니까?

스레드가 작성 될 때 OS는 각 시스템 레벨 스레드에 스택을 할당합니다. 일반적으로 OS는 언어 런타임에 의해 호출되어 애플리케이션의 힙을 할당합니다.

그들의 범위는 무엇입니까?

스택은 스레드에 연결되므로 스레드가 종료되면 스택이 재생됩니다. 힙은 일반적으로 런타임에 의해 응용 프로그램 시작시 할당되며 응용 프로그램 (기술적으로 처리)이 종료 될 때 재생됩니다.

각각의 크기는 어떻게 결정됩니까?

스택의 크기는 스레드가 생성 될 때 설정됩니다. 힙 크기는 응용 프로그램 시작시 설정되지만 공간이 필요할 때 커질 수 있습니다 (할당자는 운영 체제에서 더 많은 메모리를 요청 함).

무엇이 더 빨라 집니까?

액세스 패턴이 메모리에서 메모리를 할당 및 할당 해제하는 것이 쉽지 않기 때문에 (포인터 / 정수는 단순히 증분되거나 감소됨), 힙은 할당 또는 할당 해제와 관련하여 훨씬 더 복잡한 부기를 유지하기 때문에 스택이 더 빠릅니다. 또한 스택의 각 바이트는 매우 자주 재사용되는 경향이 있으므로 프로세서 캐시에 매핑되는 경향이있어 매우 빠릅니다. 힙에 대한 또 다른 성능 히트는 대부분 글로벌 리소스 인 힙이 일반적으로 멀티 스레딩 안전해야한다는 것입니다. 즉, 각 할당 및 할당 해제는 일반적으로 프로그램의 “모든”다른 힙 액세스와 동기화되어야합니다.

명확한 데모 :


이미지 출처 : vikashazrati.wordpress.com


답변

스택:

  • 힙처럼 컴퓨터 RAM에 저장됩니다.
  • 스택에서 생성 된 변수는 범위를 벗어나 자동으로 할당이 해제됩니다.
  • 힙의 변수에 비해 할당 속도가 훨씬 빠릅니다.
  • 실제 스택 데이터 구조로 구현되었습니다.
  • 매개 변수 전달에 사용되는 로컬 데이터, 리턴 주소를 저장합니다.
  • 너무 많은 스택을 사용하면 스택 오버플로가 발생할 수 있습니다 (주로 무한 또는 너무 깊은 재귀, 매우 큰 할당).
  • 스택에서 생성 된 데이터는 포인터없이 사용될 수 있습니다.
  • 컴파일 시간 전에 할당해야하는 데이터의 양을 정확히 알고 너무 크지 않은 경우 스택을 사용합니다.
  • 일반적으로 프로그램이 시작될 때 이미 결정된 최대 크기를 갖습니다.

더미:

  • 스택처럼 컴퓨터 RAM에 저장됩니다.
  • C ++에서 힙의 변수는 수동으로 삭제해야하며 범위를 벗어나지 않아야합니다. 데이터는 함께 해제 delete, delete[]또는 free.
  • 스택의 변수에 비해 할당 속도가 느립니다.
  • 요청시 프로그램에서 사용할 데이터 블록을 할당하는 데 사용됩니다.
  • 할당 및 할당 해제가 많은 경우 조각화가 발생할 수 있습니다.
  • C ++ 또는 C에서 힙에서 작성된 데이터는 포인터로 가리키고 new또는 malloc각각으로 할당 됩니다.
  • 너무 큰 버퍼를 할당하도록 요청하면 할당이 실패 할 수 있습니다.
  • 런타임시 필요한 데이터 양을 정확히 모르거나 많은 데이터를 할당해야하는 경우 힙을 사용합니다.
  • 메모리 누수를 담당합니다.

예:

int foo()
{
  char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
  bool b = true; // Allocated on the stack.
  if(b)
  {
    //Create 500 bytes on the stack
    char buffer[500];

    //Create 500 bytes on the heap
    pBuffer = new char[500];

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;


답변

가장 중요한 점은 힙과 스택이 메모리를 할당 할 수있는 방법에 대한 일반적인 용어라는 것입니다. 그것들은 여러 가지 방법으로 구현 될 수 있으며, 용어는 기본 개념에 적용됩니다.

  • 항목 스택에서 항목은 순서대로 순서대로 배치되며 맨 위 항목 만 제거 할 수 있습니다 (전체를 뒤집지 않고).

    종이 더미처럼 쌓아 올리십시오

    스택의 단순성은 할당 된 메모리의 각 섹션에 대한 레코드가 포함 된 테이블을 유지 관리 할 필요가 없다는 것입니다. 필요한 유일한 상태 정보는 스택 끝을 가리키는 단일 포인터입니다. 할당하고 할당을 해제하려면 해당 단일 포인터를 늘리거나 줄이면됩니다. 참고 : 스택은 때때로 메모리 섹션의 상단에서 시작하여 위쪽으로 성장하지 않고 아래쪽으로 확장되도록 구현 될 수 있습니다.

  • 힙에는 항목을 배치하는 방법에 대한 특정 순서가 없습니다. 명확한 ‘상위’항목이 없기 때문에 어떤 순서로든 항목을 가져오고 제거 할 수 있습니다.

    감초 allsorts의 힙처럼 힙

    힙 할당에는 조각화를 줄이고 오버로드 된 메모리 세그먼트를 찾아 요청 된 크기에 맞도록 충분히 큰 메모리 등을 찾기 위해 할당 된 메모리와없는 메모리에 대한 전체 레코드를 유지 관리해야합니다. 여유 공간을 남겨두고 언제든지 메모리 할당을 해제 할 수 있습니다. 때때로 메모리 할당자는 할당 된 메모리를 이동하여 메모리 조각 모음 또는 가비지 수집-메모리가 더 이상 범위에 있지 않고 할당을 해제 할 때 런타임에 식별하는 유지 관리 작업을 수행합니다.

이러한 이미지는 스택과 힙에서 메모리를 할당하고 해제하는 두 가지 방법을 설명하는 데 상당히 효과적입니다. !!

  • OS 또는 언어 런타임에 의해 어느 정도까지 제어됩니까?

    언급 한 바와 같이 힙 및 스택은 일반적인 용어이며 여러 가지 방법으로 구현할 수 있습니다. 컴퓨터 프로그램에는 일반적으로 호출 스택이라고하는 스택이 있습니다.이 스택에는 호출 된 기능에 대한 포인터 및 로컬 변수와 같은 현재 기능과 관련된 정보를 저장합니다. 함수가 다른 함수를 호출 한 다음 반환하기 때문에 스택이 커지거나 줄어들어 함수의 정보를 호출 스택 아래로 유지합니다. 프로그램에는 실제로 런타임 제어 기능이 없습니다. 프로그래밍 언어, OS 및 시스템 아키텍처에 따라 결정됩니다.

    힙은 동적으로 무작위로 할당되는 모든 메모리에 사용되는 일반적인 용어입니다. 즉 순서가 잘못되었습니다. 메모리는 일반적으로 OS에서 할당하며 응용 프로그램은 API 함수를 호출하여이 할당을 수행합니다. 동적으로 할당 된 메모리를 관리하는 데 약간의 오버 헤드가 필요하며, 일반적으로 사용되는 프로그래밍 언어 또는 환경의 런타임 코드로 처리됩니다.

  • 그들의 범위는 무엇입니까?

    콜 스택은 프로그래밍의 관점에서 ‘범위’와 관련이없는 저수준 개념입니다. 일부 코드를 분해하면 스택의 일부에 대한 상대 포인터 스타일 참조가 표시되지만 고급 언어와 관련하여 언어에는 자체 범위 규칙이 적용됩니다. 그러나 스택의 중요한 측면 중 하나는 함수가 반환되면 해당 함수의 로컬 항목이 즉시 스택에서 해제된다는 것입니다. 프로그래밍 언어가 작동하는 방식에 따라 예상대로 작동합니다. 힙에서는 정의하기도 어렵습니다. 범위는 OS에 의해 노출되는 모든 것이지만 프로그래밍 언어는 아마도 응용 프로그램에 “범위”가 무엇인지에 대한 규칙을 추가합니다. 프로세서 아키텍처와 OS는 가상 주소 지정을 사용합니다. 프로세서는 물리적 주소로 변환하고 페이지 오류 등이 있습니다. 어떤 페이지가 어떤 응용 프로그램에 속하는지 추적합니다. 그러나 프로그래밍 언어가 메모리를 할당하고 해제하는 데 사용하는 방법을 사용하고 오류를 확인하기 때문에 (어떤 이유로 든 할당 / 해제가 실패하는 경우) 실제로 걱정할 필요가 없습니다.

  • 각각의 크기는 어떻게 결정됩니까?

    다시 말하지만 언어, 컴파일러, 운영 체제 및 아키텍처에 따라 다릅니다. 스택은 일반적으로 메모리가 연속적이어야하기 때문에 일반적으로 사전 할당됩니다. 언어 컴파일러 또는 OS가 크기를 결정합니다. 스택에 방대한 양의 데이터를 저장하지 않으므로 원치 않는 끝없는 재귀 ( “스택 오버플로”) 또는 기타 비정상적인 프로그래밍 결정을 제외하고는 완전히 사용하지 않아야 할 정도로 커집니다.

    힙은 동적으로 할당 될 수있는 모든 것에 대한 일반적인 용어입니다. 당신이 그것을 보는 방법에 따라, 그것은 끊임없이 크기를 변화시키고 있습니다. 현대의 프로세서와 운영 체제에서 작동하는 정확한 방법은 매우 추상화되어 있으므로 일반적으로 (어떻게 할 수있는 언어로) 메모리를 사용하지 않아야 하는지를 제외하고는 어떻게 작동하는지에 대해 크게 걱정할 필요가 없습니다. 아직 할당하지 않았거나 해제 한 메모리가 없습니다.

  • 무엇이 더 빨라 집니까?

    사용 가능한 모든 메모리가 항상 연속적이기 때문에 스택이 더 빠릅니다. 사용 가능한 메모리의 모든 세그먼트에 대한 목록을 유지할 필요는 없으며 스택의 현재 상단에 대한 단일 포인터입니다. 컴파일러는 일반적 으로이 목적을 위해이 포인터를 특별하고 빠른 레지스터 에 저장합니다 . 또한 스택의 후속 작업은 일반적으로 메모리의 매우 가까운 영역에 집중되어 매우 낮은 수준에서 프로세서 온다이 캐시에 의한 최적화에 적합합니다.


답변

(나는이 답변을 다소 속이는 다른 질문 에서이 답변을 옮겼습니다.)

귀하의 질문에 대한 답변은 구현에 따라 다르며 컴파일러와 프로세서 아키텍처에 따라 다를 수 있습니다. 그러나 여기 간단한 설명이 있습니다.

  • 스택과 힙은 모두 기본 운영 체제에서 할당 된 메모리 영역입니다 (요청시 실제 메모리에 매핑되는 가상 메모리).
  • 다중 스레드 환경에서 각 스레드는 완전히 독립적 인 스택을 갖지만 힙을 공유합니다. 동시 액세스는 힙에서 제어되어야하며 스택에서는 불가능합니다.

  • 힙에는 사용 된 블록과 사용 가능한 블록의 링크 된 목록이 포함됩니다. 사용 가능한 블록 중 하나에서 적합한 블록을 작성 하여 힙에 대한 새 할당 ( new또는으로 malloc)이 충족됩니다. 이를 위해서는 힙의 블록 목록을 업데이트해야합니다. 힙의 블록에 대한 이 메타 정보 는 종종 모든 블록 바로 앞의 작은 영역에 힙에 저장됩니다.
  • 힙이 커짐에 따라 새로운 블록이 종종 낮은 주소에서 높은 주소로 할당됩니다. 따라서 은 메모리가 할당됨에 따라 크기가 커지는 메모리 블록 의 힙으로 생각할 수 있습니다 . 힙이 할당에 비해 너무 작은 경우 기본 운영 체제에서 더 많은 메모리를 확보하여 크기를 늘릴 수 있습니다.
  • 많은 작은 블록을 할당하고 할당 해제하면 사용 된 블록 사이에 많은 작은 여유 블록이 산재 된 상태로 힙이 남을 수 있습니다. 자유 블록의 결합 된 크기가 충분히 클 수 있더라도 자유 블록 중 어느 것도 할당 요청을 만족시키기에 충분히 큰 것이 없기 때문에 큰 블록을 할당하는 요청은 실패 할 수있다. 이것을 힙 조각화 라고 합니다.
  • 빈 블록에 인접한 사용 된 블록이 할당 해제 될 때, 새로운 빈 블록은 인접한 빈 블록과 병합되어 더 큰 빈 블록을 생성하여 힙의 단편화를 효과적으로 감소시킬 수있다.

힙

스택

  • 스택은 종종 스택 포인터 라는 CPU의 특수 레지스터와 밀접하게 작동합니다 . 처음에 스택 포인터는 스택의 맨 위 (스택에서 가장 높은 주소)를 가리 킵니다.
  • CPU는 특별 지시가 밀어 스택에 값을하고 터지는 다시 스택에서 그들을. 각 푸시 는 스택 포인터의 현재 위치에 값을 저장하고 스택 포인터를 줄입니다. 취득이 값은 스택 포인터가 가리키는 다음 (사실에 의해 혼동하지 않는 스택 포인터를 증가 추가 스택에 값하는 감소 스택 포인터 및 제거이 증가 스택에 성장 기억을. 바닥). 저장 및 검색된 값은 CPU 레지스터의 값입니다.
  • 함수가 호출되면 CPU는 현재 명령어 포인터 (예 : 스택에서 실행되는 코드 주소) 를 푸시하는 특수 명령어를 사용합니다 . 그런 다음 CPU는 명령 포인터를 호출 된 함수의 주소로 설정하여 함수로 이동합니다. 나중에 함수가 반환되면 이전 명령 포인터가 스택에서 팝되고 함수를 호출 한 직후 코드에서 실행이 다시 시작됩니다.
  • 함수가 입력되면 스택 포인터는 로컬 (자동) 변수를 위해 스택에 더 많은 공간을 할당하기 위해 줄어 듭니다. 함수에 하나의 로컬 32 비트 변수가 있으면 스택에 4 바이트가 따로 설정됩니다. 함수가 반환되면 스택 포인터가 다시 이동하여 할당 된 영역을 해제합니다.
  • 함수에 매개 변수가있는 경우 함수 호출 전에 스택에 푸시됩니다. 그런 다음 함수의 코드는 현재 스택 포인터에서 스택을 탐색하여 이러한 값을 찾을 수 있습니다.
  • 중첩 함수 호출은 매력처럼 작동합니다. 각각의 새 호출은 함수 매개 변수, 리턴 주소 및 로컬 변수에 대한 공간을 할당하며 이러한 활성화 레코드 는 중첩 된 호출에 대해 스택 될 수 있으며 함수가 리턴 될 때 올바른 방식으로 해제됩니다.
  • 스택은 제한된 메모리 블록이므로 너무 많은 중첩 함수를 호출하거나 로컬 변수에 너무 많은 공간을 할당 하여 스택 오버플 로를 일으킬 수 있습니다 . 스택에 사용되는 메모리 영역은 스택의 맨 아래 (가장 낮은 주소) 아래에 쓰면 CPU에서 트랩이나 예외가 발생하도록 설정되는 경우가 많습니다. 이 예외 조건은 런타임에 의해 포착되어 일종의 스택 오버플로 예외로 변환 될 수 있습니다.

스택

스택 대신 힙에 함수를 할당 할 수 있습니까?

아니요, 함수 (예 : 로컬 또는 자동 변수)의 활성화 레코드는 이러한 변수를 저장하고 중첩 된 함수 호출을 추적하는 데 사용되는 스택에 할당됩니다.

힙 관리 방법은 실제로 런타임 환경에 달려 있습니다. C는 사용 malloc하고 C ++는을 사용 new하지만 다른 많은 언어에는 가비지 수집이 있습니다.

그러나 스택은 프로세서 아키텍처와 밀접한 관련이있는보다 낮은 수준의 기능입니다. 충분한 공간이 없을 때 힙을 늘리는 것은 힙을 처리하는 라이브러리 호출에서 구현할 수 있으므로 너무 어렵지 않습니다. 그러나 스택 오버플로는 너무 늦을 때만 발견되므로 스택을 늘리는 것은 종종 불가능합니다. 실행 스레드를 종료하는 것이 가능한 유일한 옵션입니다.


답변

다음 C # 코드에서

public void Method1()
{
    int i = 4;
    int y = 2;
    class1 cls1 = new class1();
}

메모리 관리 방법은 다음과 같습니다.

스택의 변수 그림

Local Variables함수 호출이 스택에있는 한 지속되어야합니다. 힙은 수명이 실제로 모르지만 변수가 오래 지속되는 변수에 사용됩니다. 대부분의 언어에서 컴파일 타임에 변수를 스택에 저장하려면 변수가 얼마나 큰지를 아는 것이 중요합니다.

객체를 업데이트 할 때 크기가 다른 객체는 생성 시간에 얼마나 오래 지속 될지 알 수 없으므로 힙에 저장됩니다. 많은 언어에서 힙은 더 이상 참조가없는 객체 (예 : cls1 객체)를 찾기 위해 가비지 수집됩니다.

Java에서 대부분의 오브젝트는 힙으로 직접 이동합니다. C / C ++와 같은 언어에서는 포인터를 다루지 않을 때 구조체와 클래스가 스택에 남아있을 수 있습니다.

자세한 내용은 여기를 참조하십시오.

스택과 힙 메모리 할당의 차이점«timmurphy.org

그리고 여기:

스택 및 힙에 객체 생성

이 글은 위의 그림의 소스입니다. 6 가지 중요한 .NET 개념 : 스택, 힙, 값 유형, 참조 유형, 복싱 및 언 박싱-CodeProject

그러나 일부 부정확 한 부분이있을 수 있습니다.


답변

스택
함수를 호출하면 해당 함수에 대한 인수와 다른 오버 헤드가 스택에 추가됩니다. 일부 정보 (예 : 돌아가는 곳)도 저장됩니다. 함수 내에서 변수를 선언하면 해당 변수도 스택에 할당됩니다.

스택을 할당 해제하는 것은 할당 순서가 항상 역순이기 때문에 매우 간단합니다. 함수를 입력하면 스택 항목이 추가되고 해당 데이터를 종료하면 해당 데이터가 제거됩니다. 이것은 다른 많은 함수를 호출하는 많은 함수를 호출하거나 재귀 적 솔루션을 만들지 않는 한 스택의 작은 영역 내에 머무르는 경향이 있음을 의미합니다.


힙은 작성하는 데이터를 즉석에 배치하는 일반적인 이름입니다. 프로그램이 얼마나 많은 우주선을 만들지 모르는 경우 새로운 (또는 malloc 또는 이와 동등한) 연산자를 사용하여 각 우주선을 만들 수 있습니다. 이 할당은 잠시 동안 계속 진행될 것이므로 생성 한 순서와 다른 순서로 무료로 제공 할 수 있습니다.

따라서 힙은 훨씬 더 복잡합니다. 사용되지 않는 메모리 영역이 청크와 인터리브되어 메모리가 조각화되기 때문입니다. 필요한 크기의 여유 메모리를 찾는 것은 어려운 문제입니다. 이것이 힙을 피해야하는 이유입니다 (아직 자주 사용되지만).

구현
스택과 힙 모두의 구현은 일반적으로 런타임 / OS에 달려 있습니다. 종종 성능이 중요한 게임 및 기타 응용 프로그램은 자체 메모리 솔루션을 만들어 힙에서 많은 양의 메모리를 확보 한 다음 OS에 의존하여 메모리에 의존하지 않도록 내부적으로 제거합니다.

이것은 메모리 사용량이 표준과 매우 다른 경우에만 실용적입니다. 즉, 한 번의 큰 작업으로 레벨을로드하고 또 다른 큰 작업에서 전체 로트를 척킹 할 수있는 게임의 경우입니다.

메모리의 실제 위치 가상 메모리
라는 기술 로 인해 프로그램이 실제 데이터가 다른 곳 (하드 디스크에서도)에있는 특정 주소에 액세스 할 수 있다고 생각하게하므로 생각보다 덜 관련이 있습니다 . 콜 트리가 깊어 질수록 스택에 대한 주소가 증가합니다. 힙의 주소는 예측할 수 없으며 (즉, 암시 적 특정) 솔직히 중요하지 않습니다.


답변

명확하게하기 위해이 답변 에는 잘못된 정보가 있습니다 ( 토마스 는 의견에 따라 답변을 수정했습니다 : cool :)). 다른 답변은 정적 할당의 의미를 설명하지 않습니다. 따라서 세 가지 주요 할당 형태와 이들이 일반적으로 아래의 힙, 스택 및 데이터 세그먼트와 어떤 관련이 있는지 설명하겠습니다. 또한 사람들이 이해하도록 돕기 위해 C / C ++와 Python으로 예제를 보여줄 것입니다.

“정적”(일명 정적으로 할당 된) 변수는 스택에 할당되지 않습니다. “정적”소리가 “스택”처럼 들리기 때문에 많은 사람들이 그렇게 생각하지 마십시오. 실제로 스택이나 힙에 없습니다. 데이터 세그먼트 (data segment) 의 일부입니다 .

그러나 일반적으로 “스택”및 “힙”보다는 ” 범위 “및 ” 수명 ” 을 고려 하는 것이 좋습니다.

범위는 코드에서 변수에 액세스 할 수있는 부분을 나타냅니다. 일반적으로 범위가 훨씬 복잡 해지더라도 로컬 범위 (현재 기능으로 만 액세스 할 수 있음)와 전역 범위 (어디서나 액세스 할 수 있음)를 생각합니다.

수명은 프로그램이 실행되는 동안 변수가 할당 및 할당 해제되는시기를 나타냅니다. 일반적으로 정적 할당 (변수는 프로그램의 전체 기간 동안 지속되므로 여러 함수 호출에서 동일한 정보를 저장하는 데 유용함) 대 자동 할당 (변수는 함수를 한 번 호출하는 동안에 만 지속되므로 유용합니다. 함수 중에 만 사용되며 일단 완료되면 버릴 수있는 정보 저장) 동적 할당 (정적 또는 자동과 같은 컴파일 시간 대신 런타임에 기간이 정의 된 변수 ) 대 동적 할당 .

대부분의 컴파일러와 인터프리터는 스택, 힙 등을 사용하여 이와 유사한 방식으로이 동작을 구현하지만 동작이 올바른 경우 컴파일러가 원하는 경우 이러한 규칙을 위반할 수 있습니다. 예를 들어, 최적화로 인해 로컬 변수는 대부분 스택에 존재하더라도 레지스터에만 존재하거나 완전히 제거 될 수 있습니다. 몇 가지 의견에서 지적했듯이 스택이나 힙을 사용하지 않고 다른 스토리지 메커니즘을 사용하는 컴파일러를 자유롭게 구현할 수 있습니다 (드물게 스택과 힙이 좋기 때문에 거의 수행되지 않음).

이 모든 것을 설명하기 위해 간단한 주석이 달린 C 코드를 제공 할 것입니다. 배우는 가장 좋은 방법은 디버거에서 프로그램을 실행하고 동작을 보는 것입니다. 파이썬을 읽는 것을 선호한다면 대답의 끝으로 건너 뛰십시오. 🙂

// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;

// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;

// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {

    // Statically allocated in the data segment when the program is first loaded
    // Deallocated when the program/DLL exits
    // scope - can be accessed only within MyFunction()
    static int someLocalStaticVariable;

    // Allocated on the stack each time MyFunction is called
    // Deallocated when MyFunction returns
    // scope - can be accessed only within MyFunction()
    int someLocalVariable;

    // A *pointer* is allocated on the stack each time MyFunction is called
    // This pointer is deallocated when MyFunction returns
    // scope - the pointer can be accessed only within MyFunction()
    int* someDynamicVariable;

    // This line causes space for an integer to be allocated in the heap
    // when this line is executed. Note this is not at the beginning of
    // the call to MyFunction(), like the automatic variables
    // scope - only code within MyFunction() can access this space
    // *through this particular variable*.
    // However, if you pass the address somewhere else, that code
    // can access it too
    someDynamicVariable = new int;


    // This line deallocates the space for the integer in the heap.
    // If we did not write it, the memory would be "leaked".
    // Note a fundamental difference between the stack and heap
    // the heap must be managed. The stack is managed for us.
    delete someDynamicVariable;

    // In other cases, instead of deallocating this heap space you
    // might store the address somewhere more permanent to use later.
    // Some languages even take care of deallocation for you... but
    // always it needs to be taken care of at runtime by some mechanism.

    // When the function returns, someArgument, someLocalVariable
    // and the pointer someDynamicVariable are deallocated.
    // The space pointed to by someDynamicVariable was already
    // deallocated prior to returning.
    return;
}

// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.

수명과 범위를 구별하는 것이 중요한 이유에 대한 특별한 예는 변수가 로컬 범위를 가질 수 있지만 정적 수명을 가질 수 있다는 것입니다 (예 : 위의 코드 샘플에서 “someLocalStaticVariable”). 이러한 변수는 일반적이지만 비공식적 인 명명 습관을 매우 혼란스럽게 만듭니다. 예를 들어 ” local ” 이라고 말하면 일반적으로 ” 로컬 범위가 자동 할당 된 변수 “를 의미하고 global을 말할 때는 일반적으로 ” 전역 범위가 정적으로 할당 된 변수 “를 의미합니다. 불행히도 ” 파일 범위가 정적으로 할당 된 변수 ” 와 같은 것들에 관해서는 많은 사람들이 그냥 … ” huh ??? ” 라고 말합니다 .

C / C ++의 일부 구문 선택은이 문제를 악화시킵니다. 예를 들어 많은 사람들이 아래 표시된 구문 때문에 전역 변수가 “정적”이 아니라고 생각합니다.

int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation

int main() {return 0;}

위의 선언에 키워드 “static”을 넣으면 var2가 전역 범위를 갖지 못하게됩니다. 그럼에도 불구하고 전역 var1에는 정적 할당이 있습니다. 이것은 직관적이지 않습니다! 이러한 이유로, 범위를 설명 할 때 “정적”이라는 단어를 사용하지 말고 “file”또는 “file limited”범위와 같은 것을 말하려고합니다. 그러나 많은 사람들이 “정적”또는 “정적 범위”라는 문구를 사용하여 한 코드 파일에서만 액세스 할 수있는 변수를 설명합니다. 수명의 맥락에서 “정적”은 항상 프로그램 시작시 변수가 할당되고 프로그램이 종료 될 때 할당이 해제됨을 의미합니다.

어떤 사람들은 이러한 개념을 C / C ++에 특정한 것으로 생각합니다. 그들은 아닙니다. 예를 들어 아래의 Python 샘플은 세 가지 유형의 할당을 모두 보여줍니다 (여기서는 이해할 수없는 해석 언어에는 약간의 차이가있을 수 있음).

from datetime import datetime

class Animal:
    _FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated

    def PetAnimal(self):
        curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
        print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)

class Cat(Animal):
    _FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's

class Dog(Animal):
    _FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!


if __name__ == "__main__":
    whiskers = Cat() # Dynamically allocated
    fido = Dog() # Dynamically allocated
    rinTinTin = Dog() # Dynamically allocated

    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

    Dog._FavoriteFood = 'milkbones'
    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones