お久しぶりです。今日は C 言語の動的メモリ管理について学びます。ポインタと同じくらい重要な章ですので、よく勉強してください。
1. 動的メモリ割り当てはなぜ存在するのでしょうか?
私たちが習得した記憶力開発方法は次のとおりです。
int val = 20;//在栈空间上开辟四个字节
char arr[10] = {
0};//在栈空间上开辟大小为十个字节大小的内存,并且他们是连续的
ただし、上記のスペースを開く方法には 2 つの特徴があります。
- スペース割り当てサイズは固定です。
- 配列を宣言するときは、配列の長さを指定する必要があり、必要なメモリはコンパイル時に割り当てられます。
しかし、スペースの需要は上記の状況だけではありません。必要なスペースのサイズは、プログラムの実行中にのみ知ることができ、
配列のコンパイル時にスペースを空ける方法が満足できない場合があります。
現時点では、スペースを空けるために動的ストレージを試すことしかできません。
2. ダイナミックメモリ関数の紹介
マロック
void* malloc (size_t size);
まず、この関数の戻り値とパラメーターを理解しましょう。
戻り値は動的メモリ空間をオープンするための最初のアドレスです。オープンが成功しなかった場合は、ヌルポインタを返します。パラメータは
バイトサイズです。オープンするには 40 バイトを書き込みます
この関数は、メモリ内の連続した利用可能な領域に適用され、この領域へのポインタを返します。
- 割り当てが成功すると、割り当てられた領域へのポインタが返されます。
- オープンに失敗した場合は NULL ポインタが返されるため、malloc の戻り値を確認する必要があります。
- オープンに失敗した場合は NULL ポインタが返されるため、malloc の戻り値を確認する必要があります。
戻り値の型は void* なので、malloc 関数は開いている空間の型を知りません。使用する際はユーザーが自分で決めることができます。 - パラメータのサイズが 0 の場合、malloc の動作は標準では定義されておらず、コンパイラに依存します。
同様に、malloc を使用してスペースを解放し、free を使用してスペースを解放できます。
この関数は、malloc 関数で空けた領域を解放してメモリ領域を解放するものです。つまり、連続して使用する必要があります。そうしないとメモリ リークの問題が発生し、メモリが少しずつ使い果たされてしまいます。
free 関数は、動的に割り当てられたメモリを解放するために使用されます。
パラメータ ptr が指す空間が動的に開かれていない場合、free 関数の動作は未定義です。
パラメータ ptr が NULL ポインタの場合、関数は何も行いません。
次に、例を使用してこれら 2 つの関数の使用方法を理解しましょう。
ヘッダー ファイルは <stdlib.h> です。
#include<stdio.h>
#include<stdlib.h>
int main()
{
int arr[10] = {
0};//这是在栈上开辟的空间
int* ptr = (int*)malloc(40);//开辟十个整型
//在内存上开辟
if (ptr == NULL)
{
perror("malloc");
return 1;
}
//使用
int i = 0;
for (i = 0; i < 10; i++)
{
*(ptr + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(ptr + i));
}
//使用完之后释放
free(ptr);
ptr = NULL;
//我们要把它变成空指针,要不然可能就会变成野指针
return 0;
}
動的メモリはヒープ領域に開発されますが、ローカル変数や一時変数はスタック領域に開発されますが、これらはスタックからポップアウトされると破棄されるため、解放する必要はありません。グローバル データ、変数、静的変数を保存する静的領域もあります。
無料の重要性を説明するための例をあげましょう。この例は、よりよく理解するのに役立ちます
。、彼らは別れた (無料)、しかし、男子学生はそれに執着しています。彼は舐め犬です。彼はまだ女子クラスメートのことを覚えています電話番号を教えて、クラスメートの女子生徒に時々嫌がらせをします。住所)彼の頭を殴って、失望させ、この電話番号を忘れれば、他のクラスメートの女子生徒に嫌がらせをすることはできなくなります。
コールク
C 言語には calloc と呼ばれる関数も用意されており、これも動的メモリ割り当てに使用されます。プロトタイプは次のとおりです。
void* calloc (size_t num, size_t size);
関数の最初のパラメータは複数の要素を開くことであり、2 番目のパラメータは各要素のサイズです。実際、これは malloc に似ていますが、calloc を使用すると、開いたコンテンツは 0 に初期化されます。
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)calloc(10, sizeof(int));
if (p == NULL)
{
perror("CALLOC");
return 1;
}
free(p);
p = NULL;
return 0;
}
- この関数の機能は、サイズが size である num 個の要素用のスペースを開き、スペースの各バイトを 0 に初期化することです。
- 関数 malloc との唯一の違いは、calloc はアドレスを返す前に、要求された空間の各バイトをすべて 0 に初期化することです。
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)calloc(10, sizeof(int));
if (p == NULL)
{
perror("Calloc");
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p+i));
}
free(p);
p = NULL;
return 0;
}
再ロック
realloc 関数の登場により、動的メモリ管理がより柔軟に
過去に申請したスペースが小さすぎると感じることもあれば、申請したスペースが大きすぎると感じることもあり、適正なメモリにするためには、メモリのサイズを柔軟に調整する必要があります。次に、realloc 関数で動的に割り当てられたメモリのサイズを調整できます。
関数プロトタイプ
void* realloc (void* ptr, size_t size);
ptr は調整対象のメモリアドレス
サイズ、調整後の新しいサイズ、
戻り値は調整後のメモリの開始位置です。
この関数は、元のメモリ空間のサイズを調整することに基づいて、元のメモリ内のデータも新しい空間に移動します。
ただし、開発が失敗した場合、戻り値は上記の 2 つの異なる状況とは異なります。
- ケース 1: 元のスペースの後に十分なスペースがある
- ケース 2: 元のスペースの後に十分なスペースがない
- ケース 3: null ポインターを返します。これは正常に開かれていません。
(int*)realloc(p,80)
ここでは主に状況 2 について説明します。realloc はメモリ領域を増やすためです。たとえば、malloc は 10 個の整数を開きますが、今は 20 個必要なので、次のように書くことができます。ただし、開くときに問題が発生します。の背後に十分なスペースがないため、彼を開くことができません。その場合は、開くための新しい場所を見つけて、ヒープ領域内で開く場所を探し続ける必要があります。このとき、返されるアドレスは元のアドレスではありませんが、新しい住所。
次に、コードを見て、彼がそれをどのように使用するかを見てみましょう
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
perror("Malloc");
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
int* tmp = (int*)realloc(p, 80);
if (tmp == NULL)
{
perror("Realloc");
return 1;
}
else
{
p = tmp;
}
for (i = 0; i < 20; i++)
{
*(p + i) = i;
}
for (i = 0; i < 20; i++)
{
printf("%d ", p[i]);
}
free(p);
p = NULL;
free(tmp);
p = NULL;
return 0;
}
3.1 NULL ポインタの逆参照操作
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
例えば、ここでは p が null ポインタかどうかを判定する必要がありますが、null ポインタであれば不正アクセスという問題が発生します。
解決策: p に対して非 null 判定を行う
3.2 動的に割り当てられたスペースへの境界外アクセス
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
return 1;
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
}
したがって、書き込み後にアクセス範囲が範囲外であるかどうかを判断する必要があります
3.3 free を使用して非動的メモリを解放する
void test()
{
int a = 10;
int *p = &a;
free(p);//ok?
}
これはスタック上で開発したため、リリースする必要はありません。オペレーティング システムが自動的に破棄します。
3.4 free を使用して動的に割り当てられたメモリの一部を解放します
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
この種の問題は非常に一般的であり、初期位置を変更すると完全に解放できないため、この動的メモリを作成した最初の位置から解放する必要があります。
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
したがって、リリースごとにそれをヌルポインタに変更する必要があります
3.6 メモリを動的に開いて解放し忘れる (メモリ リーク)
コードを書き終えたときに解放するのを忘れると、メモリ リークが発生します。
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while(1);
}
解決策、解放(解放)して null ポインタに設定
今日の共有はここまでです。次号では、筆記試験の問題をいくつか共有します。