프로그램은 두 가지 주요 방법으로 컴퓨터의 주 메모리에 정보를 저장할 수 있습니다. 첫 번째는 배열, 구조 및 클래스를 포함한 전역 및 지역 변수를 사용합니다. 전역 변수와 정적 지역 변수의 경우 프로그램이 실행되는 동안 정보의 저장 위치가 고정됩니다. 지역 변수의 경우 메모리는 스택에 할당됩니다. Borland C++는 이러한 변수를 매우 효율적으로 처리하지만, 이를 사용하는 프로그래머는 프로그램 실행 중에 필요한 메모리 양을 미리 알아야 합니다.

정보를 저장하는 두 번째 방법은 Borland C++ 동적 메모리 할당 시스템을 사용하는 것입니다. 이 방법에서는 필요에 따라 여유 메모리 영역에서 정보를 저장하기 위한 메모리를 할당하고 다시 반환합니다. 더 이상 필요하지 않을 때 해제됩니다. 여유 메모리 영역은 프로그램이 있는 메모리 영역과 스택 사이에 있습니다. 이 영역을 힙이라고 하며 동적 메모리 할당 요청에 사용됩니다.

동적 메모리를 사용하는 이점은 프로그램 실행 중에 동일한 메모리를 사용하여 다른 정보를 저장할 수 있다는 것입니다. 메모리는 특정 목적을 위해 할당되고 사용이 끝나면 해제되므로 프로그램의 다른 부분에서 다른 용도로 동일한 메모리를 다른 시점에 사용할 수 있습니다. 동적 메모리 할당의 또 다른 이점은 연결 목록, 이진 트리 및 기타 동적 데이터 구조를 생성할 수 있다는 것입니다.

C의 동적 메모리 할당의 핵심은 표준 라이브러리의 일부인 malloc() 및 free() 함수입니다. malloc() 함수가 메모리 할당 요청을 할 때마다 사용 가능한 여유 메모리의 일부가 할당됩니다. free() 함수를 사용하여 이 메모리가 해제될 때마다 이 메모리는 시스템으로 다시 반환됩니다.

C++ 언어는 두 개의 동적 메모리 할당 연산자인 new와 delete를 정의합니다.

ANSI C 표준은 calloc(), malloc(), free() 및 realloc()의 네 가지 동적 메모리 할당 함수만 정의합니다. 그러나 Borland C++에는 몇 가지 다른 동적 메모리 할당 기능이 포함되어 있습니다. 최신 32비트 메모리 모델에 대한 코드를 컴파일할 때 메모리는 평평하고 일반적으로 4개의 표준 메모리 할당 기능만 사용됩니다.

ANSI C 표준은 동적 메모리 할당에 필요한 헤더 정보가 stdlib.h 파일에 포함되도록 지정합니다. 그러나 Borland C++에서는 stdlib.h 또는 alloc.h 헤더 파일을 사용할 수 있습니다. 이식성을 제공하는 stdlib.h 헤더 파일을 여기에서 사용합니다. 일부 다른 동적 메모리 할당 기능에는 alloc.h, malloc.h 또는 dos.h 헤더 파일이 필요합니다. 각 기능을 사용하기 위해 어떤 헤더 파일이 필요한지 특히 주의하십시오.

우리는 동적 메모리 할당의 가능성을 발견했습니다. 무슨 뜻인가요? 즉, 동적 메모리 할당을 사용하면 메모리가 컴파일 단계가 아니라 프로그램 실행 단계에서 예약됩니다. 이는 주로 어레이에 대해 메모리를 보다 효율적으로 할당할 수 있는 기회를 제공합니다. 에서 동적 선택메모리의 경우 배열의 크기를 미리 설정할 필요가 없습니다. 특히 배열의 크기를 항상 알 수 있는 것은 아니기 때문입니다. 다음으로 메모리 할당 방법을 살펴보겠습니다.

C에서 메모리 할당(malloc 함수)

malloc() 함수는 다음과 같이 정의됩니다. 헤더 파일 stdlib.h , 필요한 메모리 양으로 포인터를 초기화하는 데 사용됩니다. 메모리는 섹터에서 할당됩니다. 랜덤 액세스 메모리해당 시스템에서 실행 중인 모든 프로그램에서 사용할 수 있습니다. 인수는 할당할 메모리의 바이트 수이고 함수는 메모리에서 할당된 블록에 대한 포인터를 반환합니다. malloc() 함수는 다른 함수와 마찬가지로 작동하며 새로운 것은 없습니다.

왜냐하면 다른 유형데이터에는 다른 메모리 요구 사항이 있으므로 어떻게 든 데이터의 크기를 바이트 단위로 얻는 방법을 배워야 합니다. 다른 유형. 예를 들어, 우리는 int 유형의 값 배열을 위한 메모리 조각이 필요합니다. 이것은 하나의 메모리 크기이며, 동일한 크기의 배열에 메모리를 할당해야 하는 경우 이미 char 유형입니다. 다른 크기. 따라서 어떻게 든 메모리 크기를 계산해야합니다. 이것은 표현식을 받아 크기를 반환하는 sizeof() 작업으로 수행할 수 있습니다. 예를 들어 sizeof(int)는 int 값을 저장하는 데 필요한 바이트 수를 반환합니다. 예를 고려하십시오.

#포함 int *ptrVar = malloc(sizeof(int));

이 예에서 3행포인터 ptrVar에는 메모리 조각의 주소가 할당되며, 그 크기는 데이터 유형 int에 해당합니다. 자동으로 이 메모리 영역은 다른 프로그램에서 액세스할 수 없게 됩니다. 이는 할당된 메모리가 불필요해진 후에는 명시적으로 해제되어야 함을 의미합니다. 메모리가 명시적으로 해제되지 않으면 프로그램이 끝날 때 메모리가 해제되지 않습니다. 운영 체제, 이를 메모리 누수라고 합니다. null 포인터를 전달하여 할당해야 하는 할당된 메모리의 크기를 결정할 수도 있습니다. 예를 들면 다음과 같습니다.

int *ptrVar = malloc(sizeof(*ptrVar));

무슨 일이야? sizeof(*ptrVar) 작업은 포인터가 가리키는 메모리 영역의 크기를 추정합니다. ptrVar는 int 유형의 메모리 조각에 대한 포인터이므로 sizeof()는 정수 크기를 반환합니다. 즉, 실제로 포인터 정의의 첫 번째 부분에 따라 두 번째 부분의 크기가 계산됩니다. 왜 우리가 그것을 필요로합니까? 이것은 포인터의 정의를 갑자기 변경해야 하는 경우 필요할 수 있습니다(예: int ). 그런 다음 포인터 정의의 두 부분에서 데이터 유형을 변경할 필요가 없습니다. 첫 번째 부분을 변경하는 것으로 충분합니다.

플로트 *ptrVar = malloc(sizeof(*ptrVar));

보시다시피, 그러한 기록에는 매우 장점, 우리는 sizeof(float) 를 사용하여 malloc() 함수를 호출해서는 안됩니다. 대신 float 유형에 대한 포인터를 malloc() 에 전달했습니다. 이 경우 할당된 메모리의 크기는 자동으로 결정됩니다!

이것은 포인터 정의에서 멀리 떨어진 메모리를 할당해야 하는 경우 특히 유용합니다.

부동 *ptrVar; /* . . . 100줄의 코드 */ . . . ptrVar = malloc(sizeof(*ptrVar));

sizeof() 연산과 함께 메모리 할당 구조를 사용한 경우 코드에서 포인터 정의를 찾고 해당 데이터 유형을 확인한 다음에만 메모리를 올바르게 할당할 수 있습니다.

할당된 메모리 할당 해제

메모리 해제는 free() 함수로 수행됩니다. 다음은 예입니다.

무료(ptrVar);

메모리를 해제한 후 포인터를 0으로 재설정하는 것이 좋습니다(예: *ptrVar = 0 으로 설정). 포인터에 0을 할당하면 포인터가 null이 됩니다. 즉, 포인터는 더 이상 아무 곳도 가리키지 않습니다. 항상 메모리를 해제한 후 포인터에 0을 할당합니다. 그렇지 않으면 메모리를 해제한 후에도 포인터가 여전히 포인터를 가리키므로 이 메모리를 사용할 수 있는 다른 프로그램에 실수로 해를 끼칠 수 있지만 이에 대해 아무것도 모릅니다. 그러면 프로그램이 올바르게 작동한다고 생각할 것입니다.

추신: 비디오 편집을 좋아하는 사람이라면 누구나 이 Windows 7 비디오 편집기에 관심을 가질 수 있습니다. 비디오 편집기는 Movavi라고 하며 누군가 이미 익숙하거나 사용하고 있을 수도 있습니다. 러시아어로 된 이 프로그램을 사용하면 카메라에서 비디오를 쉽게 추가하고 품질과 오버레이를 향상시킬 수 있습니다. 아름다운 동영상효과.

정적 메모리프로그램 시작 전, 컴파일 및 어셈블리 단계에서 할당됩니다. 정적 변수에는 프로그램이 시작되기 전에 알려진 고정 주소가 있으며 작동 중에는 변경되지 않습니다. 정적 변수는 에 진입하기 전에 생성되고 초기화됩니다. 주요 기능프로그램 실행이 시작되는 시점.

정적 변수에는 두 가지 유형이 있습니다.

  • 전역 변수정의된 변수입니다 외부 기능, 설명에서 static이라는 단어가 누락되었습니다. 대개 설명 extern이라는 단어를 포함하는 전역 변수는 헤더 파일(h-files)로 이동됩니다. extern이라는 단어는 변수가 선언되었지만 프로그램의 이 시점에서 생성되지 않았음을 의미합니다. 정의전역 변수, 즉 extern이라는 단어가 없는 설명은 구현 파일(c-files 또는 cpp-files)에 배치됩니다. 예: 전역 변수 maxind는 두 번 선언됩니다.
    • 줄과 함께 h 파일에서

      외부 정수 maxind;

      이 선언은 그러한 변수의 존재를 보고하지만 이 변수를 생성하지는 않습니다!
    • 라인이있는 cpp 파일에서

      정수 최대값 = 1000;

      이 설명 생성 maxind 변수에 초기값 1000을 할당합니다. 언어 표준에서는 전역 변수에 초기 값을 의무적으로 할당할 필요가 없지만 그럼에도 불구하고 항상 이렇게 하는 것이 좋습니다. 그렇지 않으면 변수에 예측할 수 없는 값(프로그래머가 말하는 쓰레기)이 포함됩니다. 모든 전역 변수를 정의할 때 초기화하는 것이 좋은 스타일입니다.
    전역 변수는 프로그램의 모든 파일에서 사용할 수 있기 때문에 그렇게 명명되었습니다. 따라서 전역 변수 이름은 서로 다른 두 변수의 우연한 이름을 방지할 수 있을 만큼 충분히 길어야 합니다. 예를 들어, 전역 변수에 대한 이름 x 또는 n은 적절하지 않습니다.
  • 정적 변수설명에 static 이라는 단어가 포함된 변수입니다. 일반적으로 정적 변수가 선언됩니다. 외부 기능. 이러한 정적 변수는 모든 면에서 전역 변수와 비슷하지만 한 가지 예외가 있습니다. 정적 변수의 범위는 정의된 단일 파일로 제한되며, 게다가 선언된 후에만 사용할 수 있습니다. 아래 텍스트에서. 이러한 이유로 정적 변수 선언은 일반적으로 파일의 시작 부분에 배치됩니다. 전역 변수와 달리 정적 변수 절대 h 파일에 설명되어 있지 않습니다(extern 및 static 한정자가 서로 충돌함). 팁: 내부에 설명된 함수에서만 사용할 수 있도록 하려면 정적 변수를 사용하십시오. 같은 파일. 가능하면 이러한 상황에서 전역 변수를 사용하지 마십시오. 이렇게 하면 수백 개의 파일로 구성된 대규모 프로젝트를 구현할 때 이름 충돌을 피할 수 있습니다.
    • 정적 변수는 함수 내부에서도 선언할 수 있지만 일반적으로 아무도 그렇게 하지 않습니다. 변수는 스택에 있지 않고 정적 메모리에 있습니다. 재귀에 사용할 수 없으며 해당 값은 함수에 대한 서로 다른 입력 간에 보존됩니다. 이러한 변수의 범위는 해당 변수가 정의된 함수의 본문으로 제한됩니다. 그렇지 않으면 정적 또는 전역 변수와 유사합니다. 그것을주의해라 예어 C에서 static은 두 가지 다른 목적으로 사용됩니다.
      • 메모리 유형 표시: 변수는 스택이 아닌 정적 메모리에 있습니다.
      • 단일 파일 내에서 변수의 범위를 제한하는 방법으로(함수 외부에서 변수 선언의 경우).
  • 정적이라는 단어는 함수의 헤더에도 나타날 수 있습니다. 그러나 함수 이름의 범위를 단일 파일로 제한하는 데만 사용됩니다. 예시:

    정적 int gcd(int x, int y); // 함수 프로토타입. . . 정적 int gcd(int x, int y) ( // 구현. . . )

    팁: 사용 정적 수정자함수가 하나의 파일 내에서만 호출되는 것으로 알려진 경우 함수의 헤더에서. static이라는 단어는 함수 프로토타입에 대한 설명과 구현될 때 함수 헤더에 모두 있어야 합니다.

스택 또는 로컬 메모리

로컬 또는 스택 변수는 선언된 변수입니다. 함수 내부. 이러한 변수에 대한 메모리는 하드웨어 스택에 할당됩니다(섹션 2.3.2 참조). 메모리는 기능 또는 블록이 입력될 때 할당되고 기능 또는 블록이 종료될 때 해제됩니다. 이 경우 메모리 캡처 및 해제가 거의 즉시 발생합니다. 컴퓨터는 스택의 맨 위 주소를 포함하는 레지스터만 수정합니다.

지역 변수는 함수가 다시 입력될 때 스택에 새 지역 변수 집합이 생성되고 이전 집합이 소멸되지 않기 때문에 재귀에 사용할 수 있습니다. 같은 이유로 병렬 프로그래밍에서 스레드를 사용할 때 지역 변수는 안전합니다(섹션 2.6.2 참조). 프로그래머는 이 속성을 함수라고 부릅니다. 재진입, 영어로부터. 재입장 가능 - 재진입 가능. 이것은 프로그램의 신뢰성과 안전성 측면에서 매우 중요한 품질입니다! 정적 변수로 작동하는 프로그램에는 이 속성이 없으므로 정적 변수를 보호하려면 다음을 사용해야 합니다. 동기화 메커니즘(2.6.2 참조), 프로그램 로직은 훨씬 더 복잡해집니다. 지역 변수를 사용할 수 있다면 항상 전역 및 정적 변수를 사용하지 않아야 합니다.

지역 변수의 단점은 장점의 확장입니다. 지역 변수는 함수가 들어갈 때 생성되고, 나갈 때 사라지므로 여러 함수 간에 공유되는 데이터로 사용할 수 없습니다. 또한 하드웨어 스택의 크기는 무한하지 않으며 스택이 한 좋은 순간(예: 깊은 재귀 동안)에 오버플로되어 프로그램이 치명적인 종료로 이어질 수 있습니다. 따라서 지역 변수는 크지 않아야 합니다. 특히 큰 배열은 지역 변수로 사용할 수 없습니다.

동적 메모리 또는 힙

정적 및 스택 메모리 외에도 거의 무제한의 메모리 리소스가 있습니다. 동적, 또는 더미(힙). 프로그램은 원하는 크기의 동적 메모리 영역을 캡처할 수 있습니다. 사용 후에는 이전에 캡처한 동적 메모리 섹션을 해제해야 합니다.

동적 메모리에 할당된 공간 가상 메모리정적 메모리와 스택 간의 프로세스 (가상 메모리 메커니즘은 섹션 2.6에서 논의되었습니다.) 일반적으로 스택은 상위 가상 메모리 주소에 위치하며 주소가 감소하는 방향으로 커집니다(섹션 2.3 참조). 프로그램 및 상수 데이터는 하위 주소에 배치되고 정적 변수는 위에 배치됩니다. 정적 변수 위와 스택 아래의 공간은 동적 메모리가 차지합니다.

주소 메모리 내용

프로그램 코드와 데이터,

변화로부터 보호

...

정적 변수

프로그램들

동적 메모리

최대 주소 (2 32 -4)

스택

동적 메모리 구조는 C 또는 C++ 런타임 시스템에서 자동으로 유지 관리됩니다. 힙은 캡처된 세그먼트와 사용 가능한 세그먼트로 구성되며 각각 세그먼트 설명자가 앞에 옵니다. 메모리 잡기 요청을 실행할 때 실행 시스템은 충분한 크기의 여유 세그먼트를 검색하고 필요한 길이의 세그먼트를 잡습니다. 메모리 세그먼트가 해제되면 해제된 것으로 표시되며 필요한 경우 여러 개의 연속적인 여유 세그먼트가 결합됩니다.

C 언어는 표준 malloc 및 free 함수를 사용하여 동적 메모리를 획득 및 할당 해제합니다. 해당 프로토타입은 표준 헤더 파일 "stdlib.h"에 설명되어 있습니다. (이름 malloc은 메모리 할당- "메모리 캡처".) 이 함수의 프로토타입은 다음과 같습니다.

무효 *malloc(size_t n); // 메모리 조각을 가져옵니다. // n바이트 크기의 void free(void *p); // 메모리 영역 해제 // 주소가 p인 메모리 영역

여기서 n은 캡처된 영역의 크기(바이트)이고 size_t는 다음을 정의하는 정수 유형 중 하나의 이름입니다. 최대 크기포획된 지역. size_t 유형은 typedef 문을 사용하여 표준 헤더 파일 " stdlib.h "에 지정됩니다(117페이지 참조). 이것은 C 프로그램의 텍스트가 사용된 아키텍처와 독립적임을 보장합니다. 32비트 아키텍처에서 size_t는 부호 없는 정수로 정의됩니다.

typedef unsigned int size_t;

malloc 함수는 캡처된 메모리 영역의 주소를 반환하거나 실패 시 0을 반환합니다(사용 가능한 영역이 충분하지 않은 경우). free 함수는 주어진 주소에서 메모리 조각을 해제합니다. 포인터는 주소를 설정하는 데 사용됩니다. 일반형무효의* . malloc 함수를 호출한 후에는 캐스트 작업을 사용하여 구체적인 유형에 대한 포인터로 캐스트해야 합니다(섹션 3.4.11 참조). 예를 들어, 다음 예제는 4000바이트 힙을 잡고 1000개의 정수 배열에 대한 포인터에 해당 주소를 할당합니다.

정수*a; // 정수 배열에 대한 포인터. . . a = (int *) malloc(1000 * sizeof(int));

정수 sizeof(int)의 크기가 4바이트이기 때문에 malloc 함수 인수의 표현식은 4000입니다. 포인터를 변환하려면 제네릭 형식 포인터에서 정수에 대한 포인터로 캐스트 연산(int *)을 사용합니다.

예: 처음 n개의 소수 인쇄

동적 메모리 캡처를 사용하는 예를 살펴보겠습니다. 정수 n을 입력하고 처음 n개의 소수를 인쇄해야 합니다. (소수는 사소하지 않은 제수가 없는 숫자입니다.) 다음 알고리즘을 사용합니다. 3부터 시작하여 모든 홀수를 순차적으로 확인합니다(2는 별도로 고려). 다음 숫자를 알고리즘의 이전 단계에서 찾은 모든 소수로 나누고 검사 중인 숫자의 제곱근을 초과하지 않습니다. 이 소수로 나눌 수 없으면 그 자체가 소수입니다. 그것은 인쇄되고 발견된 소수의 배열에 추가됩니다.

필요한 소수의 수 n은 프로그램이 시작되기 전에 알 수 없기 때문에 정적 메모리에 저장할 배열을 만드는 것은 불가능합니다. 탈출구는 숫자 n을 입력한 후 이미 동적 메모리의 배열 공간을 확보하는 것입니다. 다음은 프로그램의 전체 텍스트입니다.

#포함 #포함 #포함 int main() ( int n; // 필요한 소수의 수 int k; // 현재 발견된 소수의 수 int *a; // 찾은 소수의 배열에 대한 포인터 int p; // 확인할 다음 숫자 int r; // p int i의 정수 부분 제곱근; // 소수 제수의 인덱스 bool prime; // 소수 소수 printf("소수의 수를 입력하십시오: "); scanf("%d", &n); if (n<= 0) // Некорректное значение =>반환 1; // 오류 코드와 함께 종료 // 소수 배열에 대한 메모리 캡처 a = (int *) malloc(n * sizeof(int)); a = 2; k = 1; // 배열에 2 추가 printf("%d ", a); // 그리고 그것을 출력한다 p = 3; 동안 (k< n) { // Проверяем число p на простоту r = (int)(// Целая часть корня sqrt((double) p) + 0.001); i = 0; prime = true; while (i < k && a[i] <= r) { if (p % a[i] == 0) { // p делится на a[i] prime = false; // =>p는 소수가 아닙니다. // 루프 종료 ) ++i; // 다음 소수 제수로 ) if (prime) ( // 소수를 찾으면 a[k] = p; // 배열에 추가 ++k; // 소수의 수를 늘림 printf( "%d ", p ); // 소수를 출력 if (k % 5 == 0) ( // 다음으로 전환 개행 printf("\n"); // 5개의 숫자마다 ) ) p += 2; // 다음 홀수로 ) if (k % 5 != 0) ( printf("\n"); // 문자열 번역 ) // 동적 메모리 해제 free(a); 반환 0; )

이 프로그램이 작동하는 방식의 예:

간단한 수를 입력하십시오 : 50 2 3 5 7 11 13 13 19 29 29 31 37 41 43 47 53 59 67 71 73 79 83 89 971167 173 179 181 191 193 197 197 197 197 197 197 197 197 197 197 197 197 197 197 199 211 223 227 229

C++ 신규 및 삭제 연산자

C++ 언어는 new 및 delete 연산자를 사용하여 동적 메모리를 획득하고 할당 해제합니다. C 표준 라이브러리의 일부인 malloc 및 자유 함수와 달리 C++ 언어의 일부입니다.

T 를 C 또는 C++ 유형이라고 하고 p 를 T 유형의 개체에 대한 포인터라고 합시다. 그런 다음 T 유형의 한 요소 크기로 메모리를 캡처하기 위해 new 연산자가 사용됩니다.

T*p; p = 새로운 T;

예를 들어 아래의 8바이트를 캡처하려면 실수조각은 이중 유형으로 사용됩니다.

더블 *p; p = 새로운 더블;

new 를 사용할 때 malloc 과 달리 void* 에서 원하는 유형으로 포인터를 캐스팅할 필요가 없습니다. new 연산자는 단어 new 뒤에 쓰여진 유형에 대한 포인터를 반환합니다. C 및 C++에서 두 개의 동등한 스니펫을 비교합니다.

    전역 변수와 상수를 저장합니다.

    크기는 컴파일 시간에 결정됩니다.

    스택

    지역 변수, 함수 인수 및 계산의 중간 값을 저장합니다.

    크기는 프로그램이 시작될 때 결정됩니다(보통 4MB가 할당됨).

    더미

    동적으로 할당된 메모리;

    OS는 필요에 따라 청크로 메모리를 할당합니다.

동적으로 할당된 메모리는 (프로그램을 작성할 때) 얼마나 많은 메모리가 필요할지 미리 알지 못하는 경우 사용해야 합니다(예: 배열의 크기는 프로그램이 실행되는 동안 사용자가 입력하는 항목에 따라 다름). 많은 양의 데이터로 작업할 때.

"힙"이라고도 하는 동적 메모리는 운영 체제 리소스에서 프로그램 요청 시 명시적으로 할당되며 포인터에 의해 제어됩니다. 자동으로 초기화되지 않으며 명시적으로 해제해야 합니다. 정적 및 자동 메모리와 달리 동적 메모리는 실질적으로 무제한이며(RAM 크기로만 제한됨) 프로그램이 실행되는 동안 변경될 수 있습니다.

s에서 동적 메모리 작업

C 언어에서 동적 메모리로 작업하려면 다음 함수가 사용됩니다. malloc, calloc, 무료, realloc. 더 자세히 살펴 보겠습니다.

    할당(메모리 잡기) : void *malloc(size_t size);

처럼 입력 매개변수이 함수는 할당할 메모리의 크기를 취합니다. 반환 값은 메모리의 힙 할당 청크에 대한 포인터입니다. OS가 메모리를 할당할 수 없는 경우(예: 메모리가 충분하지 않은 경우) malloc은 0을 반환합니다.

    동적으로 할당된 메모리 작업을 마친 후에는 이를 해제해야 합니다. 이를 위해 사용되는 무료 기능, OS 제어 하에 메모리를 반환합니다. void free(void *ptr);

프로그램이 종료되기 전에 동적 메모리가 해제되지 않으면 프로그램이 종료될 때 자동으로 해제됩니다. 그러나 불필요한 메모리를 명시적으로 할당 해제하는 것은 좋은 프로그래밍 스타일의 표시입니다.

예시:// 1,000개의 int 요소에 대한 메모리 할당

int * p = (int *) malloc(1000*sizeof(int));

if (p==NULL) 아웃<< "\n память не выделена";

무료(p); // 메모리를 힙에 반환

2. 할당(메모리 캡처): void *calloc(size_t nmemb, size_t size);

이 함수는 malloc과 유사하게 작동하지만 구문이 다르며(할당된 메모리의 크기 대신 요소의 수와 한 요소의 크기를 지정해야 함) 할당된 메모리가 0으로 재설정된다는 점에서 다릅니다. 예를 들어 int * p = (int *) calloc(1000, sizeof(int)) p를 실행한 후 p는 0으로 초기화된 1000개 요소의 int 배열 시작을 가리킵니다.

3. 메모리 크기 변경: void *realloc(void *ptr, size_t size);

이 함수는 할당된 메모리의 크기를 변경합니다( ptr,호출에서 파생 malloc, calloc 또는 realloc). 매개변수에 지정된 크기인 경우 크기포인터 아래에 할당된 것보다 큼 ptr,그런 다음 이미 할당된 행에서 누락된 메모리 셀을 할당할 수 있는지 여부가 확인됩니다. 공간이 충분하지 않으면 새 메모리 조각이 다음 크기로 할당됩니다. 크기및 포인터 데이터 ptr새 섹션의 시작 부분에 복사됩니다.

프로그램을 실행하는 동안 이 섹션을 지정하는 포인터를 사용할 수 있는 곳이면 어디에서나 동적 메모리 섹션을 사용할 수 있습니다. 따라서 일부 블록(예: 비주 함수의 본문)에 할당된 동적 메모리로 작업하는 다음 세 가지 변형이 가능합니다.

    포인터(동적 메모리 영역에 대한)는 로컬 자동 메모리 개체로 정의됩니다. 이 경우 포인터 지역화 블록을 종료할 때 할당된 메모리를 사용할 수 없으며 블록을 종료하기 전에 해제해야 합니다.

( int* p= (int *) calloc(n, sizeof(int))

무료(p); // 자유 dyn. 메모리

    포인터는 로컬 정적 저장소 개체로 정의됩니다. 블록에 한 번 할당된 동적 메모리는 블록이 다시 입력될 때마다 포인터를 통해 사용할 수 있습니다. 메모리는 더 이상 사용하지 않을 때만 해제해야 합니다.

(정적 int* p = (int *) calloc(n, sizeof(int));

p= (int *) calloc(n, sizeof(int));

f(50); // din을 강조 표시합니다. 해제할 메모리

f1(100); // din을 강조 표시합니다. 메모리(첫 번째 액세스)

f1(100); // din과 함께 작동합니다. 메모리

f1(0); // 자유 dyn. 메모리

    포인터는 블록에 대한 전역 개체입니다. 포인터가 "보이는" 모든 블록에서 동적 메모리를 사용할 수 있습니다. 메모리는 더 이상 사용되지 않을 때만 해제해야 합니다.

정수*pG; // din에 대한 작업 포인터. 메모리(전역 변수)

무효 초기화(int 크기)

(i=0; 나는< size; i++) //цикл ввода чисел

( printf("x[%d]=",i);

scanf("%d", &pG[i]);

정수 합계(int 크기)

(i=0; 나는< size; i++) //цикл суммирования

// 메모리 할당

pG= (int *) calloc(n, sizeof(int));

// 동적 메모리로 작업

printf(\ns=%d\n",sum(n));

무료(pG); pG=NULL; // 메모리 할당 해제

C++에서 동적 메모리 작업

C++에는 메모리 할당 및 해제를 위한 자체 메커니즘이 있습니다. 새로운그리고 삭제.사용 예 새로운: int * p = 새로운 int; // 1000 e-ths에 대한 메모리 할당 기능을 사용할 때 새로운포인터를 캐스팅할 필요가 없고 사용할 필요가 없습니다. 크기().다음을 사용하여 선택 해제 새로운메모리는 다음 호출에 의해 처리됩니다. delete p; 한 요소에 메모리를 할당해야 하는 경우 int * q = new int; 또는 int * q = new int(10); // 할당된 int는 값 10으로 초기화됩니다. 이 경우 삭제는 다음과 같습니다. delete q;

C++는 세 가지 기본 유형을 지원합니다. 배당(이상 "배포") 메모리, 우리가 이미 알고 있는 두 가지:

정적 메모리 할당및 변수를 유지합니다. 메모리는 프로그램이 시작될 때 한 번 할당되고 전체 프로그램에서 유지됩니다.

자동 메모리 할당및 에 대해 수행됩니다. 이러한 변수를 포함하는 블록에 들어갈 때 메모리가 할당되고 나갈 때 제거됩니다.

동적 메모리 할당이 수업의 주제입니다.

변수의 동적 할당

정적 및 자동 메모리 할당에는 두 가지 공통점이 있습니다.

동적 메모리 할당은 어떻게 작동합니까?

컴퓨터에는 프로그램에서 사용할 수 있는 메모리(아마도 많은 메모리)가 있습니다. 프로그램을 실행할 때 운영 체제는 해당 프로그램을 해당 메모리의 일부로 로드합니다. 그리고 프로그램에서 사용하는 이 메모리는 여러 부분으로 나뉘며 각 부분은 특정 작업을 수행합니다. 한 부분에는 코드가 포함되고 다른 부분은 일반 작업(함수 호출 추적, 전역 및 지역 변수 생성 및 소멸 등)을 수행하는 데 사용됩니다. 우리는 나중에 그것에 대해 이야기 할 것입니다. 그러나 사용 가능한 메모리의 대부분은 단순히 프로그램의 할당 요청을 기다리고 있습니다.

메모리를 동적으로 할당하면 운영 체제에서 프로그램에서 사용할 메모리 중 일부를 예약하도록 요청합니다. OS가 이 요청을 수행할 수 있으면 해당 메모리의 주소가 프로그램으로 다시 반환됩니다. 이제부터 프로그램은 이 메모리를 원하는 대로 사용할 수 있습니다. 이 메모리에 필요한 모든 작업을 이미 수행한 경우 다른 요청 간에 배포하기 위해 운영 체제로 다시 반환해야 합니다.

정적 또는 자동 메모리 할당과 달리 프로그램 자체는 동적으로 할당된 메모리를 요청하고 반환합니다.

메모리 확보

변수를 동적으로 할당할 때 또는 균일 초기화(C++11에서)를 사용하여 변수를 초기화할 수도 있습니다.

int *ptr1 = 새로운 int(7); // 직접 초기화 사용 int *ptr2 = new int ( 8 ); // 균일 초기화 사용

필요한 모든 것이 동적으로 할당된 변수로 이미 수행된 경우 이 메모리를 해제하도록 명시적으로 C++에 지시해야 합니다. 변수의 경우 다음과 같이 수행됩니다. 운영자 삭제:

// ptr이 이미 new delete ptr로 할당되었다고 가정합니다. // ptr이 가리키는 메모리를 운영 체제로 다시 반환합니다. ptr = 0; // ptr을 null로 만듭니다(C++11에서 0 대신 nullptr 사용)

삭제 연산자는 실제로 아무 것도 삭제하지 않습니다. 단순히 이전에 운영 체제에 다시 할당된 메모리를 반환합니다. 그런 다음 운영 체제는 해당 메모리를 다른 응용 프로그램(또는 동일한 응용 프로그램)에 재할당할 수 있습니다.

우리가 제거하는 것처럼 보일 수 있지만 변하기 쉬운하지만 그렇지 않다! 포인터 변수는 여전히 이전과 동일한 범위를 가지며 다른 변수와 마찬가지로 새 값을 할당할 수 있습니다.

동적으로 할당된 메모리를 가리키지 않는 포인터를 삭제하면 문제가 발생할 수 있습니다.

교수형 포인터

C++는 해제된 메모리의 내용이나 삭제되는 포인터 값에 어떤 일이 발생하는지 보장하지 않습니다. 대부분의 경우 운영 체제에 반환된 메모리에는 이전과 동일한 값이 포함됩니다. 풀어 주다, 포인터는 이미 해제된(삭제된) 메모리만 계속 가리킵니다.

해제된 메모리를 가리키는 포인터가 호출됩니다. 교수형 포인터. 댕글링 포인터를 역참조하거나 삭제하면 예기치 않은 결과가 발생합니다. 다음 프로그램을 고려하십시오.

#포함 int main() ( int *ptr = new int; *ptr = 8; // 할당된 메모리 위치에 값을 넣습니다. delete ptr; // 메모리를 운영 체제로 되돌립니다. ptr은 이제 댕글링 포인터입니다. std:: 쫓다<< *ptr; // разыменование висячего указателя приведёт к неожиданным результатам delete ptr; // попытка освободить память снова приведёт к неожиданным результатам также return 0; }

#포함

정수 메인()

int * ptr = 새로운 int ; // 정수 변수를 동적으로 할당

* 포인트 = 8 ; // 할당된 메모리 위치에 값을 넣습니다.

삭제 포인트 ; // 메모리를 운영 체제로 되돌립니다. ptr은 이제 댕글링 포인터입니다.

표준::컷<< * ptr ; // 댕글링 포인터를 역참조하면 예기치 않은 결과가 발생합니다.

삭제 포인트 ; // 메모리를 다시 해제하려고 하면 예기치 않은 결과도 발생합니다.

0 반환 ;

위의 프로그램에서 이전에 동적 변수에 할당된 값 8은 해제된 후에도 여전히 있을 수도 있고 없을 수도 있습니다. 해제된 메모리가 이미 다른 응용 프로그램(또는 운영 체제 자체 사용을 위해)에 할당되었을 수 있으며 액세스를 시도하면 운영 체제가 자동으로 프로그램을 종료합니다.

메모리를 해제하는 프로세스도 생성으로 이어질 수 있습니다. 몇몇의교수형 포인터. 다음 예를 고려하십시오.

#포함 int main() ( int *ptr = new int; // 정수 변수를 동적으로 할당 int *otherPtr = ptr; // otherPtr은 이제 ptr과 동일한 할당 메모리를 가리킵니다. delete ptr; // 메모리를 운영 체제로 되돌립니다. ptr 및 otherPtr은 이제 댕글링 포인터입니다. ptr = 0; // ptr은 이제 nullptr입니다. // 그러나 otherPtr은 여전히 ​​댕글링 포인터입니다! return 0; )

#포함

정수 메인()

int * ptr = 새로운 int ; // 정수 변수를 동적으로 할당

int * otherPtr = ptr ; // otherPtr은 이제 ptr과 동일한 할당된 메모리를 가리킵니다.

삭제 포인트 ; // 메모리를 운영 체제로 되돌립니다. ptr 및 otherPtr은 이제 댕글링 포인터입니다.

포인트 = 0 ; // ptr은 이제 nullptr입니다.

// 그러나 otherPtr은 여전히 ​​댕글링 포인터입니다!

0 반환 ;

첫째, 여러 포인터가 할당된 메모리의 동일한 부분을 가리키는 상황을 피하십시오. 이것이 가능하지 않다면 모든 포인터의 어느 포인터가 메모리를 "소유"하고(그리고 메모리를 삭제할 책임이 있는지), 어떤 포인터가 단순히 메모리에 액세스하는지 명확히 하십시오.

둘째, 포인터를 삭제할 때 포인터가 삭제 직후 종료되지 않으면 null로 만들어야 합니다. 값 0(또는 C++11에서)을 할당합니다. "삭제 직후 범위를 벗어남"이란 포인터가 선언된 블록의 맨 끝에 있는 포인터를 삭제한다는 의미입니다.

규칙: 삭제 즉시 범위를 벗어나지 않는 한 삭제된 포인터를 0(또는 C++11의 경우 nullptr)으로 설정합니다.

새로운 연산자

운영 체제에서 메모리를 요청하면 드물게 사용하지 못할 수 있습니다(즉, 사용하지 못할 수 있음).

기본적으로 new 연산자가 작동하지 않으면 메모리가 할당되지 않은 다음 예외 나쁜 할당. 이 예외가 제대로 처리되지 않으면(예외를 살펴보고 처리하지 않았기 때문에 그렇게 될 것입니다), 프로그램은 처리되지 않은 예외 오류와 함께 단순히 중단(충돌)합니다.

많은 경우에 new 연산자를 사용하여 예외를 throw하는 프로세스(프로그램 충돌과 함께)는 바람직하지 않으므로 메모리를 할당할 수 없는 경우 null 포인터를 반환하는 대체 형식의 new 연산자가 있습니다. 추가하기만 하면 됩니다. std::nothrow 상수새 키워드와 데이터 유형 사이:

int *값 = 새로운 (std::nothrow) int; // 정수 변수의 동적 할당이 실패하면 포인터 값은 null이 됩니다.

위의 예에서 new가 동적으로 할당된 메모리가 있는 포인터를 반환하지 않으면 null 포인터가 반환됩니다.

역참조도 권장하지 않습니다. 이렇게 하면 예기치 않은 결과가 발생할 수 있습니다(대부분 프로그램 충돌). 따라서 가장 좋은 방법은 메모리 할당에 대한 모든 요청을 확인하여 이러한 요청이 성공적으로 실행되고 메모리가 할당되었는지 확인하는 것입니다.

int *값 = 새로운 (std::nothrow) int; // 정수 값에 대한 동적 메모리 할당 요청 if (!value) // new가 null을 반환하는 경우 처리(즉, 메모리가 할당되지 않은 경우) ( // 이 경우 처리 std::cout<< "Could not allocate memory"; }

new 연산자가 메모리를 할당하지 않는 경우는 극히 드물기 때문에 프로그래머는 일반적으로 이 검사를 잊어버립니다!

널 포인터와 동적 메모리 할당

Null 포인터(값이 0 또는 nullptr인 포인터)는 동적 메모리 할당에 특히 유용합니다. 그들의 존재는 말하자면 "이 포인터에 할당된 메모리가 없습니다." 그리고 이것은 차례로 조건부 메모리 할당을 수행하는 데 사용할 수 있습니다.

// ptr에 아직 메모리가 할당되지 않은 경우 할당합니다. if (!ptr) ptr = new int;

널 포인터를 제거해도 아무 영향이 없습니다. 따라서 다음은 필요하지 않습니다.

(ptr) ptr을 삭제하는 경우;

만약 (ptr)

삭제 포인트 ;

대신 다음과 같이 작성할 수 있습니다.

삭제 포인트 ;

ptr이 null이 아니면 동적으로 할당된 변수가 제거됩니다. 포인터 값이 null이면 아무 일도 일어나지 않습니다.

메모리 누수

동적으로 할당된 메모리에는 범위가 없습니다. 명시적으로 해제되거나 프로그램이 실행을 종료할 때까지(그리고 운영 체제가 모든 메모리 버퍼 자체를 플러시할 때까지) 할당된 상태로 유지됩니다. 그러나 동적으로 할당된 메모리 주소를 저장하는 데 사용되는 포인터는 일반 변수 범위 지정 규칙을 따릅니다. 이 불일치는 흥미로운 동작을 유발할 수 있습니다. 예를 들어:

무효 doSomething() ( int *ptr = 새로운 int; )