web-dev-qa-db-ja.com

NULL値(または未定義)のポインターに再割り当て

私は realloc について読んでいて、そこで言及されている点について混乱しました。以下のコードを検討してください:

#include <stdio.h>
#include <stdlib.h>

int main () {

    int* ptr = NULL;
    ptr = realloc(ptr, 10*sizeof(int));
    return 0;
}

最初にrealloc値のNULLを使用してptrでメモリを割り当てることに危険はありますか?代わりに:

int* ptr = NULL;

私はこれを持っていました:

int* ptr; // no value given to ptr

reallocを使用してptrを呼び出すのは問題でしょうか?

33
user1607425

最初にNULL値のptrを使用してreallocでメモリを割り当てることには危険がありますか

None

7.22.3.5

PtrがNULLポインターの場合、realloc関数は、指定されたサイズのmalloc関数のように動作します。

2番目の部分:

int* ptr; // no value given to ptr

ptrを使用してreallocを呼び出すのは問題でしょうか?

初期化されていないポインタを使用している場合、その値が何であるかを予測できないため、それは実際に非常に深刻な問題です。関数reallocは、NULLまたはmalloc/reallocから取得した値に対してのみ正しく機能します。

それ以外の場合、ptrがメモリ管理関数によって以前に返されたポインタと一致しない場合[...]動作は未定義です。

39
cnicutar

特定のコードが示されている場合、最初にnullポインターを使用しても問題はありません。

変数ptrが初期化されていない場合-0またはNULLに設定されていない場合-を使用してrealloc()を呼び出すと危険です。動作は未定義であり、運が良ければプログラムはクラッシュしますが、運が悪ければ、しばらくの間動作しているように見え、プログラムの後で問題が発生して問題を特定するのが困難になります。ずっと前に実行されたコードで。

最初の割り当てにはmalloc()を使用し、その後はrealloc()を使用する方がよいと主張する人もいます。特に、ptr = realloc(ptr, 0);を使用してメモリを解放することはできないとしても(たとえmalloc()や[ free()は、realloc()は3つの操作すべてを実行できるため)。しかし、C90標準では、realloc(0, new_size)malloc(new_size)と同等に機能する必要があり、動作が異なるCライブラリがないことを知っています(ただし、一部のCライブラリが使用されている可能性があります。主に最も広く使用されているもの)。


ただし、次のコードなどのより一般的なケースでは、コードに微妙な問題があります(ただし、最初のnullポインターとは関係ありません)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char    *ptr = NULL;
    size_t   len = 0;
    char     buffer[256];

    while (fgets(buffer, sizeof(buffer), stdin))
    {
        size_t buflen = strlen(buffer) + 1;
        if (buflen > len)
        {
            if ((ptr = realloc(ptr, buflen)) == 0)  // Danger!
                // ... handle memory allocation failure ...
            len = buflen;
        }
        strcpy(ptr, buffer);
        // ... do something with ptr
    }
    free(ptr);
    return 0;
}

危険は何ですか?危険なのは、2回目以降のメモリ割り当てが失敗し、ptrが割り当てられたメモリへの唯一のポインタである場合、前の値をnullで上書きしただけです。つまり、ptrを使用して割り当てられたメモリを解放できなくなり、メモリリークが発生します。 (最初の割り当てでは、初期値は0、上書きされた値は0、何も変更されていません。メモリリークはありません。そのため、コードにループが追加されました。)

経験則

  • ptr = realloc(ptr, newsize);と書かないでください

テストするまで、新しい値を別の変数に保存します。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char    *ptr = NULL;
    size_t   len = 0;
    char     buffer[256];

    while (fgets(buffer, sizeof(buffer), stdin))
    {
        size_t buflen = strlen(buffer) + 1;
        if (buflen > len)
        {
            char *new_ptr = realloc(ptr, buflen);
            if (new_ptr == 0)
                // ... handle memory allocation failure ...
            ptr = new_ptr;
            len = buflen;
        }
        strcpy(ptr, buffer);
        // ... do something with ptr
    }
    free(ptr);
    return 0;
}

このコードは、割り当ての失敗時にメモリをリークしません。

補助的な推奨事項:newという変数を使用しないでください。 C++コンパイラでコンパイルするのが難しくなります。現在C++に変換するつもりがない場合でも(おそらく、メモリ管理を書き直すことになりますが)、C++キーワードnewをC変数名として使用することには利点がありません。 C++コンパイラでのコンパイルを明示的に禁止する場合を除きます。

8

最初にNULL値のptrを使用してreallocを使用してメモリを割り当てることに危険はありますか?

いいえ、それはmallocとまったく同じです。

代わりに:

int* ptr = NULL;

私はこれを持っていました:

int* ptr; // no value given to ptr

ptrを使用してreallocを呼び出すのは問題でしょうか?

はい、問題があります。 reallocNULLを取得しない場合、その場所からメモリを拡張しようとするか、またはmayfreemallocのメモリの別の部分を試します。 初期化されていない変数は任意の値をとることができるであるため、確率は非常に高く、reallocのような値ではありません。運が良ければ、プログラムはすぐにクラッシュします。

4
Shahbaz