動的メモリ管理
1.なぜ動的メモリ割り当てが必要なのですか?
以前に使用されたメモリ開発方法は、スタック上のスペースを開くことです。次に例を示します。
int val = 20;//在栈空间上开辟四个字节
char arr[10] = {
0};//在栈空间上开辟10个字节的连续空间
ただし、上記のスペースを開く方法には、次の2つの特徴があります。
- スペースのサイズは固定されています。
- 配列を宣言するときは、配列の長さを指定する必要があり、必要なメモリはコンパイル時に割り当てられます。
しかし、スペースの需要は上記の場合だけではありません。必要なスペースのサイズは、プログラムの実行中にしかわからず、配列のコンパイル方法が満たされない場合があります。現時点では、動的なストレージと開発のみを試すことができます。
2、動的メモリ機能の紹介
2.1malloc
mallocは動的メモリを開くために使用されます
void * malloc(size_t size);
この関数は、メモリから継続的に使用可能なスペースに適用され、このスペースへのポインタを返します。
- あなたが開いた場合は成功し、それは宇宙開くために良い点を返すポインタを。
- 開発が失敗した場合、NULLポインターが返されるため、mallocの戻り値を確認する必要があります。
- 戻り値のタイプはvoid *であるため、malloc関数は開かれたスペースのタイプを認識せず、ユーザーはそれをいつ使用するかを決定します。
- パラメータサイズが0の場合、mallocの動作は標準では定義されておらず、コンパイラによって異なります。
2.2無料
void free (void* ptr);
free関数は、動的に割り当てられたメモリを解放するために使用されます。
- パラメータptrが指すスペースが動的に開かれていない場合、free関数の動作は未定義です。
- パラメータptrがNULLポインタの場合、関数は何もしません。
mallocとfreeの両方がstdlib.hヘッダーファイルで宣言されています。例えば:
//申请5个整形字节的动态内存
int *p = (int *)malloc(5*sizeof(int));
//若返回值为空,则申请失败
if (NULL == p)
{
printf("malloc error!\n");
return 1;
}
//若申请成功,为每个元素赋值
for (int i = 0; i < 5; i++)
{
p[i] = i;
}
printf("malloc success!\n");
//打印释放前p的地址
printf("before:%p\n",p);
//释放动态开辟的内存
free(p);
//打印释放后p的地址
printf("after:%p\n", p);
ここで注意すべきいくつかのポイント
- 上図のように、解放前後のアドレスは変更されず、ポインタと対応するメモリの管理関係のみが変更されます。通常、別のコードを追加して、ポインターをNULLにします。
p = NULL;
- 下の図に示すように、解放されたアドレス空間は、アプリケーション時のポインタに他のメタ情報が含まれているため、アプリケーション時の空間よりも明らかに大きくなっています。mallocを使用して小さなスペースブロックを適用する方がコストがかかるため、小さなメモリブロックを適用することはお勧めしません。
2.3 calloc
C言語は、動的メモリ割り当てにも使用されるcallocと呼ばれる関数も提供します。プロトタイプは次のとおりです。
void* calloc (size_t num, size_t size);
- この関数の機能は、サイズサイズのnum要素のスペースを開き、スペースの各バイトを0に初期化することです。
- 関数mallocとの違いは、callocは、アドレスを返す前に、要求されたスペースの各バイトをすべて0に初期化することです。例えば:
int *p = (int *)calloc(10, sizeof(int));
if (NULL != p)
{
printf("success!\n");
}
free(p);
p = NULL;
2.4 realloc
realloc関数の出現により、動的メモリ管理がより柔軟になります。
過去に申請したスペースが小さすぎると感じることもあれば、申請するスペースが大きすぎると感じることもあります。メモリを合理的に使用するには、メモリのサイズを柔軟に調整する必要があります。realloc関数は、動的に開かれるメモリのサイズを調整できます。関数プロトタイプ:
void * realloc(void * ptr, size_t size);
- ptrは、調整するメモリアドレスです。
- サイズ調整後の新しいサイズ
- 戻り値は、調整されたメモリの開始位置です。
- この関数は、メモリスペースの元のサイズに基づいて調整するだけでなく、元のメモリ内のデータを新しいスペースに移動します。
- reallocがメモリスペースを調整する場合、2つの状況があります。
- 状況1:元のスペースの後に十分なスペースがあります。ケース1の場合、メモリを拡張する場合は、元のメモリの直後にスペースを追加すると、元のスペースのデータは変更されません。
- 状況2:元のスペースの後に十分なスペースがありません。拡張方法:使用するヒープスペース上で、適切なサイズの別の連続したスペースを見つけます。この関数は、新しいメモリアドレスを返します。上記の2つの状況のため、realloc関数の使用には注意が必要です。例
:
int *ptr = malloc(100);
if (ptr != NULL)
{
//业务处理
}
else
{
exit(EXIT_FAILURE);
}
//扩展容量
//代码1
ptr = realloc(ptr, 1000);//如果申请失败造成内存泄漏
//代码2
int*p = NULL;
p = realloc(ptr, 1000);
if (p != NULL)
{
ptr = p;
}
//业务处理
free(ptr);
コード1の問題は、reallocアプリケーションが失敗してNULLポインターを返すと、古いスペースptrが空になり、メモリリークが発生し、元のメモリアドレスが見つからないことです。
3つの一般的な動的メモリエラー
3.1NULLポインターの逆参照操作
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
要求されたスペースが大きい場合、mallocの戻り値はNULLになる可能性があります
3.2動的なオープンスペースへの範囲外アクセス
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
}
3.3動的に開かれていないメモリには無料リリースを使用する
void test()
{
int a = 10;
int *p = &a;
free(p);//错误
}
3.4動的に開かれたメモリの一部を解放するためにfreeを使用する
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
3.5同じ動的メモリを複数回解放する
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);
}