05-C言語の高度な動的メモリ管理

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);
}

おすすめ

転載: blog.csdn.net/qq_40076022/article/details/110109813