C言語の詳しい解説 - 動的メモリ割り当て (1)

目次

動的メモリ割り当てはなぜ存在するのでしょうか?

動的メモリ関数の概要

マロック

無料

コールク

再ロック

一般的な動的メモリエラー

1. NULL ポインタの逆参照操作

2. 動的に開かれた空間への境界外アクセス

3. 非動的に割り当てられたメモリには自由解放を使用します。

4. free を使用して、動的に割り当てられたメモリの一部を解放します。

5. 同じ動的メモリを複数回解放する

6. 動的に割り当てられたメモリと解放の忘れ (メモリ リーク)


動的メモリ割り当てはなぜ存在するのでしょうか?

一般的なメモリ割り当て方法:
int val = 20;//スタック領域に 4 バイトを割り当てます
char arr[10] = {0};//スタック領域に 10 バイトの連続領域を空ける

 上記のスペースを開く方法には 2 つの特徴があります。

  • スペースの開口部のサイズは固定されています
  • 配列を宣言するときは、配列の長さを指定する必要があり、必要なメモリはコンパイル時に割り当てられます。

この従来の空間作成方法は柔軟性に欠けており、指定されたサイズの空間を作成したり、作成された空間のサイズを調整したりすることができません。

ここで、動的メモリ割り当てが役に立ちます。

動的メモリ関数の概要

  • マロック

動的メモリを開くための関数malloc : void* malloc (size_t size); は、 stdlib.hヘッダー ファイルで宣言されています

この関数はメモリ内の連続した利用可能な領域に適用され、 `malloc` 関数を使用して必要なメモリ領域を割り当てます。`malloc` は 1 つの引数、つまり割り当てたいバイト数を取ります。割り当てられたメモリ ブロックへのポインタを返します返されたポインタを必要なデータ型に変換することを忘れないでください。

int* p = (int*)malloc(40);
  • パラメーターのサイズ0の場合mallocの動作は標準では定義されておらず、コンパイラーに依存します。 
  • 開発に失敗するとNULLポインタが返されるため、mallocの戻り値を確認する必要があります。

まず、メモリ パーティションを紹介します。

メモリはスタック領域、ヒープ領域、静的領域に分かれています

  • スタック領域はローカル変数として使用されます
  • ヒープ領域は動的メモリ割り当てと仮パラメータに使用されます。
  • 静的領域はグローバル変数と静的変数に使用されます。

 この例を見てみましょう:

int main()
{
	//int arr[10];
	int* p = (int*)malloc(40);

	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//开辟成功
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d\n", *(p + i));
	}

	free(p);
	p = NULL;

	return 0;
}

 40 バイトのスペースを正常に開き、10 バイトのスペースにアクセスしました。 

  • このコードは、malloc関数を使用してメモリのヒープ領域に 40 バイトの空間を空け、その開いた空間の先頭アドレスをスタック領域の p に格納します。開いたスペースから、このスペースにアクセスできます。

  • malloc が空間に適用された後、空間の開始アドレスを直接返し、空間の内容は初期化されません。

  •  動的に割り当てられたメモリを使用する場合 malloc割り当てが失敗すると(通常はメモリ不足が原因で)、malloc ヌル ポインタ (NULL) が返されます。(p == NULL) がヌルかどうかを判断し、ヌルの場合は、エラーを使用しますエラーメッセージを出力する機能。

  • perror関数が何なのかまだわからない場合は、 perror 関数に関する私の記事を読んでください
  • malloc によって要求された領域は、プログラムの終了時にオペレーティング システムに返されます。プログラムが終了しない場合、動的に適用されたメモリは積極的に解放されません。このとき、無料の機能を利用して解除する必要があります。
  • 無料

    free 関数は、動的に割り当てられたメモリを解放するために使用されます。 stdlib.hヘッダー ファイル で宣言されます
  • パラメーターp が指すスペースが動的に割り当てられない場合、free関数の動作は未定義です。
  • パラメーターp がNULLポインターの場合、関数は何も行いません。
  • free を使用すると、パラメータ p はワイルド ポインタになります。これを null ポインタに変換し、NULL 値を割り当てることができます。
  • コールク

calloc関数は、動的メモリ割り当てにも使用されます。void* calloc ( size_t num , size_t size );

  • callocはstdlib.hヘッダー ファイルで宣言されます
  • この関数の機能は、サイズ sizeのnum要素用のスペースを開き、スペースの各バイトを0に初期化することです
  • 関数mallocとの唯一の違いは、 calloc はアドレスを返す前に、要求された空間の各バイトをすべて 0 に初期化することです。
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 )、 realloc は stdlib.hヘッダー ファイル で宣言されます。
  • ptrは調整するメモリアドレスです
  • サイズ調整後の新しいサイズ
  • 戻り値は調整後のメモリ開始位置です。
  • この関数は、元のメモリ空間のサイズを調整するだけでなく、元のメモリ内のデータを新しい空間に移動します。
int main()
{
    int* p=(int*)malloc(40);
    if(p == NULL)
    {
        perror("malloc");
        return 1;
    }

    for(int i=0;i<10;i++)
        p[i] = i + 1;

    p = realloc(p,80);
    return 0;
}

 

後ろに十分なスペースがある場合は、後ろにスペースを直接追加し、古いスペース p の開始アドレスを返します。元のスペースのデータは変更されません

  • 新しいスペースを開いて必要なバイトを空けます
  • 古いスペースのデータが新しいスペースにコピーされます
  • 古いスペースを解放する
  • 新しいスペースの開始アドレスを返します

ケース 3:オープンに失敗して null ポインタを返す

realloc がオープンに失敗し、null ポインタを返した場合、p = realloc(p,80); このような代入により、p の元のデータが直接失われます。別の変数を作成して、realloc の戻り値を受け取ることができます。同時に、開発が失敗し、戻り値が null ポインタであるかどうかを判断するための if ステートメントが追加されます。

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//初始化为1~10
	int i = 0;
	for (i = 0; i < 10; i++) 
	{
		p[i] = i + 1;
	}
	//增加一些空间
	int* ptr = (int*)realloc(p, 8000);
	if (ptr != NULL)
	{
		p = ptr;
		ptr = NULL;
	}
	else
	{
		perror("realloc");
		return 1;
	}
	//打印数据
	for (i = 0; i < 20; i++)
	{
		printf("%d\n", p[i]);
	}
	//释放
	free(p);
	p = NULL;

	return 0;
}

デバッグして ptr と p のアドレスを見てみましょう。 ptr が realloc 空間を受け取るとき、この時点のアドレスは次のとおりです。

 

p = ptr まで実行するときに、p のスペースが不十分な場合、realloc は p 用のスペースを開き、p のアドレスは ptr と同じになります。

一般的な動的メモリエラー

1.  NULLポインタの逆参照操作

void test()
{
    int *p = (int *)malloc(INT_MAX/4);
    *p = 20;
    free(p);
}

p の値が NULL の場合は問題があるため、p が NULL ポインタであるかどうかを判断する必要があります。

if (p == NULL)
	{
		perror("malloc");
		return 1;
	}

2. 動的に開かれた空間への境界外アクセス

void test()
{
    int i = 0;
    int *p = (int *)malloc(10*sizeof(int));
    if(NULL == p)
    {
        exit(EXIT_FAILURE);    
    }
    for(i=0; i<20; i++)
    {
        *(p+i) = i;//当i大于10的时候越界访问
    }
    free(p);
    p = NULL;
}

合計 10 個の整数スペースが割り当てられますが、20 個の整数データにアクセスしようとすると、範囲外アクセスが発生します。 

3.非動的に割り当てられたメモリには自由解放を使用します。

void test()
{
    int a = 10;    
    int *p = &a;
    free(p);//ok?
}

4. freeを使用して、動的に割り当てられたメモリの一部を解放します。

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*p = i;
		p++;
	}
	//释放
	free(p);
	p = NULL;

	return 0;
}

ポインタが開始位置を指していません。 

5. 同じ動的メモリを複数回解放する

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		return 1;
	}
	//使用

	free(p);
	free(p);
	return 0;
}

  p = NULL; メモリを解放した後は良い習慣を身につけ、p を null ポインタに代入してください。 

6. 動的に割り当てられたメモリと解放の忘れ (メモリ リーク)

void test()
{
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
}

int main()
{
	test();
	while (1);

	return 0;
}

p は関数のローカル変数であり、関数の終了時に破棄されますが、malloc によって要求された領域はまだ存在しており、解放できません。

動的に適用されたメモリ空間はスコープ外になるため、自動的に破棄されません(オペレーティング システムに返されます)。

破壊する方法は 2 つだけです。

  1. 無料リリース
  2. プログラム終了(終了)

まとめ:

 学習への道は長くて大変です。復習とコーディングを続けていただければ幸いです。将来、お気に入りのオファーが必ず届くでしょう。

 次の学習に進みましょう:C言語の詳細説明 – 動的メモリ割り当て (2)

おすすめ

転載: blog.csdn.net/m0_73800602/article/details/133156874