문제

검색했지만이 세 가지 개념을 잘 이해하지 못했습니다. 언제 동적 할당 (힙에)을 사용해야하고 실제 이점은 무엇입니까? 정적 및 스택의 문제는 무엇입니까? 힙에 변수를 할당하지 않고 전체 응용 프로그램을 작성할 수 있습니까?

다른 언어가 "쓰레기 수집가"를 포함한다고 들었으므로 기억에 대해 걱정할 필요가 없습니다. 쓰레기 수집가는 무엇을합니까?

이 쓰레기 수집가를 사용하여 할 수없는 일을 직접 조작 할 수 있습니까?

누군가 나 에게이 선언으로 그 말을하자 :

int * asafe=new int;

"포인터에 대한 포인터"가 있습니다. 무슨 뜻인가요? 다릅니다.

asafe=new int;

?

도움이 되었습니까?

해결책

비슷한 질문 요청을 받았지만 정적에 대해 묻지 않았습니다.

정적, 힙 및 스택 메모리의 요약 :

  • 정적 변수는 기본적으로 전역으로 액세스 할 수 없더라도 기본적으로 글로벌 변수입니다. 일반적으로 실행 파일 자체에있는 주소가 있습니다. 전체 프로그램에는 하나의 사본 만 있습니다. 기능 호출 (또는 클래스)에 몇 번이나 들어가더라도 (스레드 수) 변수는 동일한 메모리 위치를 참조합니다.

  • 힙은 동적으로 사용할 수있는 많은 메모리입니다. 객체에 대해 4KB를 원한다면 동적 할당자는 힙에 여유 공간 목록을 살펴보고 4KB 청크를 골라서주십시오. 일반적으로, 동적 메모리 할당 기 (Malloc, New, et c.)는 메모리 끝에서 시작하여 뒤로 작동합니다.

  • 스택이 어떻게 자라나고 줄어든 방법을 설명하는 것은이 답의 범위를 벗어나지 만 항상 끝에서만 추가하고 제거한다고 말하면 충분합니다. 스택은 일반적으로 시작하여 낮은 주소로 자랍니다. 스택이 중간 어딘가에 동적 할당자를 충족 할 때 메모리가 부족합니다 (그러나 물리적 대상 메모리 및 조각화를 참조하십시오). 여러 스레드에는 여러 스택이 필요합니다 (프로세스는 일반적으로 스택의 최소 크기를 보유합니다).

각각을 사용하고 싶을 때 :

  • Statics/Globals는 항상 필요하다는 것을 알고있는 메모리에 유용하며 거래를하고 싶지 않다는 것을 알고 있습니다. (그건 그렇고, 임베디드 환경은 정적 메모리 만있는 것으로 생각 될 수 있습니다 ... 스택과 힙은 세 번째 메모리 유형에 의해 공유되는 알려진 주소 공간의 일부입니다. 프로그램 코드. 프로그램은 종종 동적 할당을 수행합니다. 정적 메모리 링크 된 목록과 같은 것들이 필요할 때의 정적 메모리. 그러나 정적 메모리 자체 (버퍼) 자체는 "할당 된"것이 아니라 다른 객체 가이 목적을 위해 버퍼가 보유한 메모리에서 할당됩니다.이를 수행 할 수 있습니다. 비 에베드드에서도 콘솔 게임에서 모든 할당에 사전 설정 크기의 버퍼를 사용하여 할당 프로세스를 엄격하게 제어하기 위해 내장 된 동적 메모리 메커니즘을 종종 피할 수 있습니다.)

  • 스택 변수는 함수가 범위에있는 한 (어딘가에 스택에) 변수를 유지하기를 원할 때 유용합니다. 스택은 코드가 위치한 코드에 필요한 변수에 적합하지만 해당 코드 외부에는 필요하지 않습니다. 또한 파일과 같은 리소스에 액세스 할 때도 정말 좋습니다. 해당 코드를 떠날 때 리소스가 자동으로 사라지기를 원합니다.

  • 힙 할당 (동적으로 할당 된 메모리)은 위의 것보다 유연성이 높을 때 유용합니다. 종종 이벤트에 응답하도록 함수가 호출됩니다 (사용자는 "상자 작성"버튼을 클릭합니다). 적절한 응답을 위해서는 함수가 종료 된 후에도 오랫동안 붙어야하는 새 개체 (새 상자 개체)를 할당해야하므로 스택에있을 수 없습니다. 그러나 프로그램 시작시 원하는 상자가 얼마나 많은지 알지 못하므로 정적이 될 수 없습니다.

쓰레기 수집

나는 최근에 쓰레기 수집가가 얼마나 훌륭한 지에 대해 많이 들었으므로, 약간의 반대하는 목소리가 도움이 될 것입니다.

쓰레기 수집은 성능이 큰 문제가되지 않을 때에 대한 훌륭한 메커니즘입니다. 나는 GC가 점점 더 정교 해지고 있다고 들었지만 사실, 당신은 유스 케이스에 따라 성과 페널티를 받아 들여야 할 수도 있습니다. 게으른 경우 여전히 제대로 작동하지 않을 수 있습니다. 최상의 시간에 쓰레기 수집가는 더 이상 언급이 없다는 것을 알게되면 기억이 사라지는 것을 알고 있습니다 ( 참조 계산). 그러나 자신을 지칭하는 객체가있는 경우 (아마도 다시 참조되는 다른 객체를 참조하여) 참조 계산만으로 메모리를 삭제할 수 있음을 나타내지 않습니다. 이 경우 GC는 전체 참조 수프를보고 자체적으로 만 언급되는 섬이 있는지 알아 내야합니다. Offhand, 나는 O (n^2) 운영이라고 생각하지만, 그것이 무엇이든간에 성능에 관심이 있으면 나빠질 수 있습니다. (편집 : 마틴 b 지적 합리적으로 효율적인 알고리즘의 경우 O (n)입니다. 성능에 관심이 있고 쓰레기 수집없이 일정한 시간에 거래 할 수 있다면 여전히 O (n)가 너무 많습니다.)

개인적으로, 사람들이 C ++에 쓰레기 수집이 없다고 말할 때, 내 마음은 C ++의 특징으로 태그를 지정하지만 아마도 소수 일 것입니다. 사람들이 C 및 C ++에서 프로그래밍에 대해 배우는 가장 어려운 것은 포인터와 동적 메모리 할당을 올바르게 처리하는 방법 일 것입니다. Python과 같은 다른 언어는 GC 없이는 끔찍할 것이므로 언어에서 원하는 것에 달려 있다고 생각합니다. 신뢰할 수있는 성능을 원한다면 쓰레기 수집이없는 C ++만이 내가 생각할 수있는 Fortran의 유일한 것입니다. 사용 편의성과 훈련 휠을 원한다면 ( "적절한"메모리 관리를 배우지 않고 충돌하지 않도록) GC로 무언가를 선택하십시오. 메모리를 잘 관리하는 방법을 알고 있더라도 다른 코드를 최적화 할 수있는 시간을 절약 할 수 있습니다. 더 이상 성능 페널티가 많지 않지만, 신뢰할 수있는 성능이 필요하다면 (그리고 표지 아래에서 무슨 일이 일어나고 있는지 정확히 알 수있는 능력) C ++를 고수 할 것입니다. 내가 들어 본 모든 주요 게임 엔진이 C ++ (C 또는 어셈블리가 아닌 경우)에있는 이유가 있습니다. Python 등은 스크립팅에 적합하지만 기본 게임 엔진은 아닙니다.

다른 팁

다음은 물론 모두 정확하지 않습니다. 읽을 때 소금 한 알로 가져 가십시오 :)

글쎄, 당신이 언급 한 세 가지는입니다 자동, 정적 및 동적 스토리지 지속 시간, 그것은 대상이 얼마나 오래 살고 있고 언제 인생을 시작하는지와 관련이 있습니다.


자동 저장 시간

자동 저장 시간을 사용합니다 짧은 살았습니다 그리고 작은 데이터 만 필요합니다 장소 상에서 일부 블록 내에서 :

if(some condition) {
    int a[3]; // array a has automatic storage duration
    fill_it(a);
    print_it(a);
}

우리가 블록을 나가 자마자 수명이 끝나고 객체가 정의 되 자마자 시작됩니다. 그것들은 가장 간단한 스토리지 지속 시간이며 특히 동적 스토리지 지속 시간보다 훨씬 빠릅니다.


정적 저장 시간

귀하는 해당 범위 (네임 스페이스 범위)를 허용하는 경우 모든 코드로 액세스 할 수있는 무료 변수에 정적 저장 시간을 사용하고 범위 (로컬 범위) 및 범위의 종료에서 수명을 연장 해야하는 로컬 변수에 대해서는 모든 코드로 액세스 할 수 있습니다. 클래스의 모든 객체 (클래스 범위)에서 공유 해야하는 멤버 변수의 경우. 그들의 수명은 그들이있는 범위에 달려 있습니다. 그들은 가질 수 있습니다. 네임 스페이스 범위 그리고 로컬 범위 그리고 클래스 범위. 두 사람 모두에 대한 사실은 생명이 시작되면 평생이 끝나는 것입니다. 프로그램의 끝. 두 가지 예는 다음과 같습니다.

// static storage duration. in global namespace scope
string globalA; 
int main() {
    foo();
    foo();
}

void foo() {
    // static storage duration. in local scope
    static string localA;
    localA += "ab"
    cout << localA;
}

프로그램은 인쇄합니다 ababab, 왜냐하면 localA 블록이 나면 파괴되지 않습니다. 로컬 범위가있는 개체는 수명을 시작한다고 말할 수 있습니다. 제어가 그들의 정의에 도달 할 때. 을 위한 localA, 기능의 본문이 들어 오면 발생합니다. 네임 스페이스 범위의 객체의 경우 수명이 시작됩니다 프로그램 시작. 클래스 범위의 정적 객체에 대해서도 마찬가지입니다.

class A {
    static string classScopeA;
};

string A::classScopeA;

A a, b; &a.classScopeA == &b.classScopeA == &A::classScopeA;

보시다시피, classScopeA 계급의 특정 대상이 아니라 클래스 자체에 묶여 있습니다. 위의 세 이름의 주소는 모두 동일하며 모두 동일한 객체를 나타냅니다. 정적 객체가 언제 어떻게 초기화되는지에 대한 특별한 규칙이 있지만 지금은 걱정하지 마십시오. 그것은 용어의 의미입니다 정적 초기화 순서 FIASCO.


동적 스토리지 지속 시간

마지막 저장 시간은 동적입니다. 다른 섬에 객체를 살고 싶다면 그것을 사용하고 해당 참조 주위에 포인터를 넣고 싶습니다. 객체가있는 경우에도 사용합니다 , 그리고 당신이 알려진 크기의 배열을 만들고 싶다면 실행 시간. 이러한 유연성으로 인해 동적 스토리지 지속 시간이있는 물체는 복잡하고 관리가 느립니다. 동적 지속 시간을 갖는 물체는 적절한 경우 수명을 시작합니다. 새로운 운영자 호출이 발생합니다.

int main() {
    // the object that s points to has dynamic storage 
    // duration
    string *s = new string;
    // pass a pointer pointing to the object around. 
    // the object itself isn't touched
    foo(s);
    delete s;
}

void foo(string *s) {
    cout << s->size();
}

그 수명은 당신이 전화 할 때만 끝납니다 삭제 그들을 위해. 당신이 그것을 잊어 버리면, 그 물건들은 결코 평생 끝나지 않습니다. 그리고 사용자 선언 된 생성자를 정의하는 클래스 객체에는 소멸자가 호출되지 않습니다. 동적 스토리지 지속 시간이있는 객체에는 수명 및 관련 메모리 자원의 수동 처리가 필요합니다. 라이브러리는 사용을 쉽게하기 위해 존재합니다. 명백한 쓰레기 수집 ~을 위한 특정 대상 스마트 포인터를 사용하여 설정할 수 있습니다.

int main() {
    shared_ptr<string> s(new string);
    foo(s);
}

void foo(shared_ptr<string> s) {
    cout << s->size();
}

삭제를 호출 할 필요가 없습니다. 공유 PTR이 당신을 위해 그것을합니다. 객체를 참조하는 마지막 포인터가 범위를 벗어나지 않습니다. 공유 PTR 자체에는 자동 저장 시간이 있습니다. 그래서 그것의 수명은 자동으로 관리되므로 파괴자의 동적 대상을 삭제 해야하는지 확인할 수 있습니다. shared_ptr 참조는 부스트 문서를 참조하십시오. http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/shared_ptr.htm

"짧은 대답"과 마찬가지로 정교하게 알려져 있습니다.

  • 정적 변수 (클래스)
    수명 = 프로그램 런타임 (1)
    가시성 = 액세스 수정 자 (개인/보호/공개)에 의해 결정

  • 정적 변수 (글로벌 범위)
    수명 = 프로그램 런타임 (1)
    가시성 = 컴파일 장치는 (2)에 인스턴스화됩니다.

  • 힙 변수
    Lifetime = 귀하가 정의했습니다 (삭제하기 시작)
    가시성 = 귀하에 의해 정의 된 (포인터를 할당하든)

  • 스택 변수
    가시성 = 선언에서 범위가 종료 될 때까지
    수명 = 선언에서 범위가 종료 될 때까지


(1) 더 정확하게 : 초기화에서 컴파일 장치의 부진화까지 (예 : C / C ++ 파일). 컴파일 단위의 초기화 순서는 표준에 의해 정의되지 않습니다.

(2) 조심 : 헤더에 정적 변수를 인스턴스화하는 경우 각 컴파일 장치는 자체 사본을 얻습니다.

나는 Pedants 중 하나가 곧 더 나은 답을 얻을 것이라고 확신하지만, 주요 차이점은 속도와 크기입니다.

스택

할당하는 것이 훨씬 빠릅니다. 스택 프레임을 설정할 때 할당되므로 기본적으로 무료이므로 O (1)에서 수행됩니다. 단점은 스택 공간이 부족하면 뼈가 나온다는 것입니다. 스택 크기를 조정할 수 있지만 iirc는 ~ 2MB를 가지고 놀 수 있습니다. 또한 기능을 종료하자마자 스택의 모든 것이 지워집니다. 따라서 나중에 언급하는 것은 문제가 될 수 있습니다. (할당 된 객체를 쌓는 포인터는 버그로 이어집니다.)

더미

할당하는 것이 크게 느립니다. 그러나 당신은 함께 놀고 가리킬 GB를 가지고 있습니다.

쓰레기 수집가

쓰레기 수집기는 백그라운드에서 실행되고 메모리를 해방시키는 일부 코드입니다. 힙에 메모리를 할당하면 메모리 누출로 알려진 메모리를 제거하는 것이 매우 쉽습니다. 시간이 지남에 따라 응용 프로그램이 소비하는 메모리가 충돌 할 때까지 성장하고 성장합니다. 쓰레기 수집가가 주기적으로 메모리를 자유롭게 해제하면 더 이상 필요하지 않은 메모리를 사용하면이 클래스의 버그를 제거하는 데 도움이됩니다. 물론 이것은 쓰레기 수집가가 물건을 느리게함에 따라 가격으로 나옵니다.

정적 및 스택의 문제는 무엇입니까?

"정적"할당의 문제점은 컴파일 타임에 할당이 이루어진다는 것입니다. 런타임까지 알려지지 않은 일부 가변 수의 데이터를 할당하는 데 사용할 수 없습니다.

"스택"을 할당하는 문제는 할당이 반환되는 서브 루틴이 즉시 할당이 파괴된다는 것입니다.

힙에 변수를 할당하지 않고 전체 응용 프로그램을 쓸 수 있습니까?

아마도 사소한, 정상적인 대형 응용 프로그램은 아니지만 (소위 "내장 된"프로그램은 C ++의 하위 집합을 사용하여 힙없이 작성 될 수 있음).

쓰레기 수집가는 무엇을합니까?

응용 프로그램이 더 이상 참조되지 않을 때를 감지하기 위해 데이터 ( "Mark and Sweep")를 계속보고 있습니다. 응용 프로그램이 데이터를 처리 할 필요가 없기 때문에 응용 프로그램에 편리합니다. 그러나 쓰레기 수집기는 계산 비용이 많이들 수 있습니다.

쓰레기 수집기는 C ++ 프로그래밍의 일반적인 기능이 아닙니다.

이 쓰레기 수집가를 사용하여 할 수없는 일을 직접 조작 할 수 있습니까?

결정 론적 메모리 거래를위한 C ++ 메커니즘을 배우십시오.

  • '정적': 절대 처리하지 마십시오
  • '스택': 변수가 "범위를 벗어나 자마자"
  • '힙': 포인터가 삭제 될 때 (응용 프로그램에 의해 명시 적으로 삭제되거나 일부 또는 기타 서브 루틴 내에서 암시 적으로 삭제)

스택 메모리 할당 (함수 변수, 로컬 변수)은 스택이 너무 "깊이"상태 일 때 문제가 될 수 있고 스택 할당에 사용할 수있는 메모리를 오버플레이합니다. 힙은 여러 스레드에서 또는 프로그램 라이프 사이클 전체에서 액세스 해야하는 물체에 대한 것입니다. 힙을 사용하지 않고 전체 프로그램을 작성할 수 있습니다.

쓰레기 수집기 없이는 메모리를 아주 쉽게 누출 할 수 있지만 물체와 메모리가 해제 될 때도 지시 할 수도 있습니다. GC를 실행할 때 Java와 관련된 문제를 해결했으며 GC는 독점적 인 스레드이기 때문에 실시간 프로세스가 있습니다 (아무것도 실행할 수 없습니다). 따라서 성능이 중요하고 유출 된 물체가 없다고 보장 할 수 있다면 GC를 사용하지 않는 것이 매우 도움이됩니다. 그렇지 않으면 응용 프로그램이 메모리를 소비하고 누출 소스를 추적해야 할 때 인생을 미워하게 만듭니다.

프로그램이 할당 할 메모리의 양을 모르는 경우 (따라서 스택 변수를 사용할 수 없음)? 링크 된 목록을 말하면, 목록은 크기가 무엇인지 모르고 성장할 수 있습니다. 따라서 힙에 할당하는 것은 링크 된 목록에 얼마나 많은 요소를 삽입 할 것인지 알지 못할 때 링크 된 목록에 적합합니다.

어떤 상황에서 GC의 장점은 다른 상황에서는 성가심입니다. GC에 대한 의존은 그것에 대해 많이 생각하지 않습니다. 이론적으로, '유휴'기간이 될 때까지 또는 앱에서 대역폭을 훔치고 응답 대기 시간을 유발할 때까지 기다립니다.

그러나 당신은 '그것에 대해 생각하지 않을 필요가 없습니다.' 멀티 스레드 앱의 다른 모든 것과 마찬가지로, 양보 할 수 있으면 양보 할 수 있습니다. 예를 들어 .NET에서 GC를 요청할 수 있습니다. 이 작업을 수행하면 GC가 덜 자주 실행되는 대신 GC를 실행하는 더 짧은 수치를 가질 수 있으며이 오버 헤드와 관련된 대기 시간을 확산시킬 수 있습니다.

그러나 이것은 GC의 주요 매력을 물리칩니다. GC는 "자동 매트 -Ic이기 때문에 그것에 대해 많이 생각하지 않아도된다"고 GC의 주요 매력을 물리칩니다.

GC가 널리 퍼지기 전에 처음으로 프로그래밍에 노출되어 Malloc/Free 및 New/Delete에 익숙해지면 GC가 약간 성가 시거나 불신이 될 수도 있습니다 (불신이 될 수 있듯이 '). 최적화, '체크 무늬 이력을 가지고 있습니다.) 많은 앱은 임의의 대기 시간을 견딜 수 있습니다. 그러나 임의의 대기 시간이 덜 허용되지 않는 앱의 경우, 일반적인 반응은 GC 환경을 피하고 순전히 관리되지 않은 코드 (또는 오랜 예술, 조립 언어)의 방향으로 이동하는 것입니다.

나는 여름 학생을 잠시 거슬러 올라간 인턴, 똑똑한 아이를 낳았다. 그는 MALLOC/FREE NEW/DELETE 모델을 따르기를 거부했기 때문에 "MALLOC/FREE NEW/DELETE 모델을 따르기를 거부하더라도 GC의 초자기에 대해 너무나 인기를 얻었습니다. 그리고 당신은 알고 있습니까? 작고 짧은 실행 앱의 경우 실제로이를 벗어날 수는 있지만 오랫동안 실행되는 성능 앱에는 적합하지 않습니다.

스택은 컴파일러에 의해 할당 된 메모리이며, 프로그램을 컴파일 할 때마다 기본 컴파일러에서 OS에서 일부 메모리를 할당합니다 (IDE의 컴파일러 설정에서 설정을 변경할 수 있음). 시스템에 가용 한 많은 메모리와 다른 많은 것들에 대해, 스택 메모리에 오는 것은 우리가 복사 한 변수 (공식으로 참조)를 선언 할 때 할당됩니다. 이러한 변수는 비주얼 스튜디오에서 기본적으로 CDECL을 통해 일부 명명 규칙을 따릅니다. 예 : Infix 표기법 : C = A+B; 스택 푸시는 왼쪽으로 오른쪽으로 왼쪽으로 밀고, b는 스택, 연산자, a to stack 및 그 결과를 쌓아 놓은 결과입니다. 사전 수정 표기법 : =+택시 여기서 모든 변수는 1st (오른쪽에서 왼쪽)로 눌린 다음 작동이 이루어집니다. 컴파일러로 할당 된이 메모리는 고정되어 있습니다. 따라서 1MB의 메모리가 우리의 응용 프로그램에 할당되었다고 가정하자. 그리고이 스택은 함수의 범위가 끝날 때이 스택이 지워지는 수명 시간이 적습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top