나는 한동안 프로그래밍을 해왔지만 대부분 Java와 C #이었습니다. 실제로 혼자서 메모리를 관리 할 필요가 없었습니다. 최근에 C ++로 프로그래밍을 시작했는데 스택에 저장해야 할 시점과 힙에 저장해야 할 시점에 대해 약간 혼란 스러웠습니다.
내 이해는 매우 자주 액세스되는 변수는 스택과 객체에 저장되어야하고, 거의 사용되지 않는 변수와 큰 데이터 구조는 모두 힙에 저장되어야한다는 것입니다. 이것이 맞습니까 아니면 내가 틀렸습니까?
나는 한동안 프로그래밍을 해왔지만 대부분 Java와 C #이었습니다. 실제로 혼자서 메모리를 관리 할 필요가 없었습니다. 최근에 C ++로 프로그래밍을 시작했는데 스택에 저장해야 할 시점과 힙에 저장해야 할 시점에 대해 약간 혼란 스러웠습니다.
내 이해는 매우 자주 액세스되는 변수는 스택과 객체에 저장되어야하고, 거의 사용되지 않는 변수와 큰 데이터 구조는 모두 힙에 저장되어야한다는 것입니다. 이것이 맞습니까 아니면 내가 틀렸습니까?
답변:
아니요, 스택과 힙의 차이는 성능이 아닙니다. 수명입니다 : 함수 내부의 모든 지역 변수 (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;
}
스택이 무엇인지 더 명확하게 이해하려면 스택이 높은 수준의 언어로 무엇을하는지 이해하려고하지 말고 "콜 스택"및 "호출 규칙"을 찾아보고 무엇을 확인하십시오. 기계는 함수를 호출 할 때 실제로 수행합니다. 컴퓨터 메모리는 일련의 주소 일뿐입니다. "힙"과 "스택"은 컴파일러의 발명품입니다.
내가 말할 것:
가능한 경우 스택에 저장하십시오.
필요한 경우 힙에 저장하십시오.
따라서 힙보다 스택을 선호하십시오. 스택에 무언가를 저장할 수없는 몇 가지 가능한 이유는 다음과 같습니다.
합리적인 컴파일러를 사용하여 힙 (일반적으로 컴파일 시간에 크기를 알 수없는 배열)에 고정되지 않은 크기 개체를 할당 할 수 있습니다.
다른 답변이 제안하는 것보다 더 미묘합니다. 선언 방법에 따라 스택의 데이터와 힙의 데이터 사이에는 절대적인 구분이 없습니다. 예를 들면 :
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 ++의 적용 가능성을 벗어난 것일 수 있습니다.)
또한 항목이 생성 된 함수의 범위 밖에서 사용되어야하는 경우 힙에 항목을 저장합니다. 스택 객체와 함께 사용되는 관용구 중 하나는 RAII입니다. 이것은 스택 기반 객체를 자원의 래퍼로 사용하는 것과 관련이 있습니다. 객체가 파괴되면 자원이 정리됩니다. 스택 기반 개체는 예외가 발생할 수있는시기를 추적하기가 더 쉽습니다. 예외 처리기에서 힙 기반 개체를 삭제하는 데 신경 쓸 필요가 없습니다. 이것이 최신 C ++에서 원시 포인터가 일반적으로 사용되지 않는 이유입니다. 힙 기반 객체에 대한 원시 포인터에 대한 스택 기반 래퍼가 될 수있는 스마트 포인터를 사용합니다.
다른 답변에 추가하기 위해 적어도 약간의 성능에 관한 것일 수도 있습니다. 당신과 관련이없는 한 이것에 대해 걱정할 필요는 없지만 :
힙에 할당하려면 일정 시간 작업이 아닌 메모리 블록을 추적해야합니다 (일부주기와 오버 헤드가 필요함). 메모리가 조각화되거나 주소 공간을 100 % 사용하는 데 가까워지면 속도가 느려질 수 있습니다. 반면에 스택 할당은 일정 시간이며 기본적으로 "무료"작업입니다.
고려해야 할 또 다른 사항 (다시 말하지만 문제가되는 경우에만 중요 함)은 일반적으로 스택 크기가 고정되어 있으며 힙 크기보다 훨씬 작을 수 있다는 것입니다. 따라서 큰 개체 나 작은 개체를 많이 할당하는 경우 힙을 사용하고 싶을 것입니다. 스택 공간이 부족하면 런타임에서 사이트 제목 예외가 발생합니다. 일반적으로 큰 문제는 아니지만 고려해야 할 또 다른 사항입니다.
스택은 더 효율적이고 관리하기 쉬운 범위 데이터입니다.
그러나 힙은 몇 KB 보다 큰 모든 것에 사용해야합니다 (C ++에서는 간단 boost::scoped_ptr
합니다. 할당 된 메모리에 대한 포인터를 보유하기 위해 스택에 생성하기 만하면 됩니다).
자신을 계속 호출하는 재귀 알고리즘을 고려하십시오. 전체 스택 사용량을 제한하거나 추측하는 것은 매우 어렵습니다! 반면 힙에서 할당 자 ( malloc()
또는 new
)는 return NULL
또는 throw
ing을 통해 메모리 부족을 나타낼 수 있습니다 .
출처 : 스택이 8KB 이하인 Linux Kernel!
std::unique_ptr
Boost와 같은 외부 라이브러리보다 선호되어야 하는를 제공합니다 (시간이 지남에 따라 표준에 정보를 제공하지만).
힙 또는 스택에 할당할지 여부는 변수가 할당되는 방식에 따라 선택됩니다. "new"호출을 사용하여 동적으로 무언가를 할당하면 힙에서 할당하는 것입니다. 무언가를 전역 변수로 할당하거나 함수의 매개 변수로 할당하면 스택에 할당됩니다.
아마도 이것은 아주 잘 대답되었습니다. 낮은 수준의 세부 사항을 더 깊이 이해하기 위해 아래 일련의 기사를 알려 드리고자합니다. Alex Darby는 디버거로 안내하는 일련의 기사를 보유하고 있습니다. 스택에 대한 3 부입니다. http://www.altdevblogaday.com/2011/12/14/cc-low-level-curriculum-part-3-the-stack/