C ++에서 적절한 스택 및 힙 사용?


122

나는 한동안 프로그래밍을 해왔지만 대부분 Java와 C #이었습니다. 실제로 혼자서 메모리를 관리 할 필요가 없었습니다. 최근에 C ++로 프로그래밍을 시작했는데 스택에 저장해야 할 시점과 힙에 저장해야 할 시점에 대해 약간 혼란 스러웠습니다.

내 이해는 매우 자주 액세스되는 변수는 스택과 객체에 저장되어야하고, 거의 사용되지 않는 변수와 큰 데이터 구조는 모두 힙에 저장되어야한다는 것입니다. 이것이 맞습니까 아니면 내가 틀렸습니까?


답변:


242

아니요, 스택과 힙의 차이는 성능이 아닙니다. 수명입니다 : 함수 내부의 모든 지역 변수 (malloc () 또는 new가 아닌 ​​모든 것)는 스택에 있습니다. 함수에서 돌아 오면 사라집니다. 선언 한 함수보다 더 오래 살기를 원하면 힙에 할당해야합니다.

class Thingy;

Thingy* foo( ) 
{
  int a; // this int lives on the stack
  Thingy B; // this thingy lives on the stack and will be deleted when we return from foo
  Thingy *pointerToB = &B; // this points to an address on the stack
  Thingy *pointerToC = new Thingy(); // this makes a Thingy on the heap.
                                     // pointerToC contains its address.

  // this is safe: C lives on the heap and outlives foo().
  // Whoever you pass this to must remember to delete it!
  return pointerToC;

  // this is NOT SAFE: B lives on the stack and will be deleted when foo() returns. 
  // whoever uses this returned pointer will probably cause a crash!
  return pointerToB;
}

스택이 무엇인지 더 명확하게 이해하려면 스택이 높은 수준의 언어로 무엇을하는지 이해하려고하지 말고 "콜 스택"및 "호출 규칙"을 찾아보고 무엇을 확인하십시오. 기계는 함수를 호출 할 때 실제로 수행합니다. 컴퓨터 메모리는 일련의 주소 일뿐입니다. "힙"과 "스택"은 컴파일러의 발명품입니다.


7
다양한 크기의 정보가 일반적으로 힙에 저장된다는 점을 추가하는 것이 안전합니다. 내가 아는 유일한 예외는 C99의 VLA (지원이 제한적 임)와 C 프로그래머조차도 종종 오해하는 alloca () 함수입니다.
Dan Olson

10
좋은 설명이지만 할당 및 / 또는 할당 해제가 빈번한 다중 스레드 시나리오에서는 힙 경합 지점이므로 성능에 영향을줍니다. 그럼에도 불구하고 Scope는 거의 항상 결정적인 요소입니다.
peterchen

18
물론 new / malloc () 자체는 느린 작업이며 스택은 임의의 힙 라인보다 dcache에있을 가능성이 더 높습니다. 이것은 실제 고려 사항이지만 일반적으로 수명 문제에 부차적입니다.
Crashworks

1
"컴퓨터 메모리는 일련의 주소 일뿐입니다."힙 "과"스택 "은 컴파일의 발명품입니다." 나는 스택이 우리 컴퓨터 메모리의 특별한 영역 인 많은 곳에서 읽었습니다.
Vineeth Chitteti

2
@kai 그것은 그것을 시각화하는 방법이지만 반드시 물리적으로 말한 것은 아닙니다. OS는 애플리케이션의 스택 및 힙 할당을 담당합니다. 컴파일러도 책임이 있지만 주로 OS에 의존합니다. 스택은 제한되며 힙은 없습니다. 이는 OS가 이러한 메모리 주소를보다 구조화 된 것으로 정렬하여 여러 응용 프로그램이 동일한 시스템에서 실행될 수 있도록 처리하는 방식 때문입니다. 힙과 스택이 유일한 것은 아니지만 일반적으로 대부분의 개발자가 염려하는 유일한 두 가지입니다.
tsturzl

42

내가 말할 것:

가능한 경우 스택에 저장하십시오.

필요한 경우 힙에 저장하십시오.

따라서 힙보다 스택을 선호하십시오. 스택에 무언가를 저장할 수없는 몇 가지 가능한 이유는 다음과 같습니다.

  • 너무 큽니다. 32 비트 OS의 멀티 스레드 프로그램에서 스택은 작고 고정 된 (최소한 스레드 생성시) 크기 (일반적으로 몇 메가에 불과합니다. 이는 주소를 소모하지 않고 많은 스레드를 생성 할 수 있도록하기위한 것입니다) 64 비트 프로그램 또는 단일 스레드 (어쨌든 Linux) 프로그램의 경우 이는 큰 문제가 아닙니다. 32 비트 Linux에서 단일 스레드 프로그램은 일반적으로 힙의 맨 위에 도달 할 때까지 계속 성장할 수있는 동적 스택을 사용합니다.
  • 원래 스택 프레임의 범위 밖에서 액세스해야합니다. 이것이 실제로 주된 이유입니다.

합리적인 컴파일러를 사용하여 힙 (일반적으로 컴파일 시간에 크기를 알 수없는 배열)에 고정되지 않은 크기 개체를 할당 할 수 있습니다.


1
일반적으로 2KB 이상의 것은 힙에 넣는 것이 가장 좋습니다. 구체적인 사항은 모르지만 "몇 메가"인 스택으로 작업 한 적이 없었습니다.
Dan Olson

2
이것은 처음에는 사용자에게 관심이없는 부분입니다. 사용자에게는 벡터와 목록이 STL이 힙에 내용을 저장하더라도 스택에 할당 된 것처럼 보입니다. 질문은 명시 적으로 new / delete를 호출 할시기를 결정하는 줄에 더 많은 것 같았습니다.
David Rodríguez-dribeas

1
Dan : 32 비트 리눅스에서 스택에 2 기가 (예, GIGS에서 G)를 넣었습니다. 스택 제한은 OS에 따라 다릅니다.
Mr. Ree

6
mrree : Nintendo DS 스택은 16 킬로바이트입니다. 일부 스택 제한은 하드웨어에 따라 다릅니다.
개미

Ant : 모든 스택은 하드웨어, OS 및 컴파일러에 따라 다릅니다.
Viliami

24

다른 답변이 제안하는 것보다 더 미묘합니다. 선언 방법에 따라 스택의 데이터와 힙의 데이터 사이에는 절대적인 구분이 없습니다. 예를 들면 :

std::vector<int> v(10);

함수의 본문 vector에서 스택에있는 10 개의 정수로 구성된 (동적 배열) 을 선언합니다 . 그러나 스토리지는vector 는 스택에 없습니다.

아, 그러나 (다른 답변에서 제안) 해당 스토리지의 수명은 vector 은 스택 기반 인 자체 되므로 구현 방법에 차이가 없습니다. 스택 기반 객체로만 취급 할 수 있습니다. 가치 의미론으로.

별로. 함수가 다음과 같다고 가정합니다.

void GetSomeNumbers(std::vector<int> &result)
{
    std::vector<int> v(10);

    // fill v with numbers

    result.swap(v);
}

그래서 무엇이든 swap 함수 모든 것 (복잡한 값 유형에는 하나가 있어야 함)은 해당 데이터의 단일 소유자를 보장하는 시스템에서 일부 힙 데이터에 대한 일종의 리 바인딩 가능한 참조 역할을 할 수 있습니다.

따라서 현대 C ++ 접근 방식에있다 결코 네이 키드 로컬 포인터 변수에 힙 데이터의 주소를 저장 입니다. 모든 힙 할당은 클래스 내부에 숨겨져 야합니다.

그렇게하면 프로그램의 모든 변수를 단순한 값 유형 인 것처럼 생각할 수 있으며 힙에 대해 완전히 잊어 버릴 수 있습니다 (비정상적이어야하는 일부 힙 데이터에 대해 새 값과 유사한 래퍼 클래스를 작성할 때 제외). .

최적화하는 데 도움이되는 특별한 지식을 하나만 유지하면됩니다. 가능한 경우 다음과 같이 한 변수를 다른 변수에 할당하는 대신 :

a = b;

다음과 같이 교체하십시오.

a.swap(b);

훨씬 더 빠르고 예외를 발생시키지 않기 때문입니다. 유일한 요구 사항은 b동일한 값을 계속 유지할 필요가 없다는 것입니다 ( a대신의 값 을 가져 오며에서 폐기 됨 a = b).

단점은이 접근 방식이 실제 반환 값 대신 출력 매개 변수를 통해 함수에서 값을 반환하도록한다는 것입니다. 그러나 그들은 rvalue 참조 로 C ++ 0x에서 수정하고 있습니다.

가장 복잡한 상황에서이 아이디어를 일반적인 극단으로 가져 가서 shared_ptr이미 tr1에있는 것과 같은 스마트 포인터 클래스를 사용합니다 . (필요한 것 같으면 표준 C ++의 적용 가능성을 벗어난 것일 수 있습니다.)


6

또한 항목이 생성 된 함수의 범위 밖에서 사용되어야하는 경우 힙에 항목을 저장합니다. 스택 객체와 함께 사용되는 관용구 중 하나는 RAII입니다. 이것은 스택 기반 객체를 자원의 래퍼로 사용하는 것과 관련이 있습니다. 객체가 파괴되면 자원이 정리됩니다. 스택 기반 개체는 예외가 발생할 수있는시기를 추적하기가 더 쉽습니다. 예외 처리기에서 힙 기반 개체를 삭제하는 데 신경 쓸 필요가 없습니다. 이것이 최신 C ++에서 원시 포인터가 일반적으로 사용되지 않는 이유입니다. 힙 기반 객체에 대한 원시 포인터에 대한 스택 기반 래퍼가 될 수있는 스마트 포인터를 사용합니다.


5

다른 답변에 추가하기 위해 적어도 약간의 성능에 관한 것일 수도 있습니다. 당신과 관련이없는 한 이것에 대해 걱정할 필요는 없지만 :

힙에 할당하려면 일정 시간 작업이 아닌 메모리 블록을 추적해야합니다 (일부주기와 오버 헤드가 필요함). 메모리가 조각화되거나 주소 공간을 100 % 사용하는 데 가까워지면 속도가 느려질 수 있습니다. 반면에 스택 할당은 일정 시간이며 기본적으로 "무료"작업입니다.

고려해야 할 또 다른 사항 (다시 말하지만 문제가되는 경우에만 중요 함)은 일반적으로 스택 크기가 고정되어 있으며 힙 크기보다 훨씬 작을 수 있다는 것입니다. 따라서 큰 개체 나 작은 개체를 많이 할당하는 경우 힙을 사용하고 싶을 것입니다. 스택 공간이 부족하면 런타임에서 사이트 제목 예외가 발생합니다. 일반적으로 큰 문제는 아니지만 고려해야 할 또 다른 사항입니다.


힙과 스택은 모두 페이징 된 가상 메모리입니다. 힙 검색 시간은 새 메모리에 매핑하는 데 걸리는 시간에 비해 엄청나게 빠릅니다. 32 비트 Linux에서는> 2gig를 스택에 넣을 수 있습니다. Mac에서는 스택이 65Meg로 하드 제한되어 있다고 생각합니다.
Mr. Ree

3

스택은 더 효율적이고 관리하기 쉬운 범위 데이터입니다.

그러나 힙은 KB 보다 큰 모든 것에 사용해야합니다 (C ++에서는 간단 boost::scoped_ptr합니다. 할당 된 메모리에 대한 포인터를 보유하기 위해 스택에 생성하기 만하면 됩니다).

자신을 계속 호출하는 재귀 알고리즘을 고려하십시오. 전체 스택 사용량을 제한하거나 추측하는 것은 매우 어렵습니다! 반면 힙에서 할당 자 ( malloc()또는 new)는 return NULL또는 throwing을 통해 메모리 부족을 나타낼 수 있습니다 .

출처 : 스택이 8KB 이하인 Linux Kernel!


다른 독자를 위해 : (A) 여기에서 "해야 할 것"은 순전히 사용자의 개인적인 의견이며, 많은 사용자가 접할 가능성이없는 최대 1 개의 인용과 1 개의 시나리오 (재귀)에서 추출한 것입니다. 또한 (B) 표준 라이브러리는 std::unique_ptrBoost와 같은 외부 라이브러리보다 선호되어야 하는를 제공합니다 (시간이 지남에 따라 표준에 정보를 제공하지만).
underscore_d


1

힙 또는 스택에 할당할지 여부는 변수가 할당되는 방식에 따라 선택됩니다. "new"호출을 사용하여 동적으로 무언가를 할당하면 힙에서 할당하는 것입니다. 무언가를 전역 변수로 할당하거나 함수의 매개 변수로 할당하면 스택에 할당됩니다.


4
나는 그가 언제 힙에 물건을 올려 놓을 지 묻고 있었을 것입니다.
Steve Rowe

0

제 생각에는 두 가지 결정 요소가 있습니다

1) Scope of variable
2) Performance.

대부분의 경우 스택을 사용하는 것을 선호하지만 범위 외부의 변수에 액세스해야하는 경우 힙을 사용할 수 있습니다.

힙을 사용하는 동안 성능을 향상시키기 위해 힙 블록을 생성하는 기능을 사용할 수도 있으며, 이는 각 변수를 다른 메모리 위치에 할당하는 대신 성능을 얻는 데 도움이 될 수 있습니다.


0

아마도 이것은 아주 잘 대답되었습니다. 낮은 수준의 세부 사항을 더 깊이 이해하기 위해 아래 일련의 기사를 알려 드리고자합니다. Alex Darby는 디버거로 안내하는 일련의 기사를 보유하고 있습니다. 스택에 대한 3 부입니다. http://www.altdevblogaday.com/2011/12/14/cc-low-level-curriculum-part-3-the-stack/


링크가 죽은 것처럼 보이지만 Internet Archive Wayback Machine을 확인하면 스택에 대해서만 이야기하므로 여기서 스택에 대한 특정 질문에 답하기 위해 아무것도하지 않음을 나타냅니다. 힙에. -1
underscore_d
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.