web-dev-qa-db-ja.com

Cの関数からローカル変数を返す

#include <stdio.h>

int foo1(void)
{
    int p;
    p = 99;
    return p;
}

char *foo2(void)
{
    char buffer[] = "test_123";
    return buffer;
}

int *foo3(void)
{
    int t[3] = {1,2,3};
    return t;
}

int main(void)
{
    int *p;
    char *s;

    printf("foo1: %d\n", foo1());
    printf("foo2: %s\n", foo2());
    printf("foo3: %d, %d, %d\n", p[0], p[1], p[2]);
    return 0;
}

gcc -ansi -pedantic -W -Wallでこれをコンパイルすると、コンパイラはfoo2()およびfoo3()の警告メッセージを発行します。

warning: function returns address of local variable

ローカル変数を返すことは許可されていないと思いましたが、foo1()は正常に動作し、ローカルオブジェクトへのポインターを返すこととオブジェクト自体に大きな違いがあるようです。

誰もがこの問題に光を当てることができますか?前もって感謝します!

34
Mark

ここでの問題は、ローカル変数を作成するとスタックに割り当てられるため、関数の実行が完了すると使用できないことです(実装はここで異なります)。望ましい方法は、malloc()を使用して非ローカルメモリを予約することです。ここでの危険は、free()を使用して割り当てたすべての割り当てを解除(malloc())する必要があり、忘れるとメモリリークが発生することです。

24
kelloti

foo1()の場合、ローカル変数自体ではなく、ローカル変数のcopyを返します。

他の関数については、ローカル変数へのポインターのコピーを返します。ただし、関数の終了時にそのローカル変数の割り当てが解除されるため、後で参照しようとすると厄介な問題が発生します。

18
Cam

どの変数もメモリ内にいくらかのスペースがあります。ポインターはそのスペースを参照します。ローカル変数が占有するスペースは、関数呼び出しが戻るときに割り当て解除されます。つまり、他の目的で再利用できることを意味します。結果として、その空間への参照は、完全に無関係な何かを指し示すことになります。 Cの配列はポインターとして実装されているため、これに適用されます。また、関数で宣言された定数配列もローカルとしてカウントされます。

作成された関数のスコープを超えて配列または他のポインターを使用する場合は、mallocを使用してそのためのスペースを予約する必要があります。 mallocを使用して予約されたスペースは、freeを呼び出して明示的に解放されるまで、再割り当てまたは再利用されません。

5
Keith Irwin

はい、実際には舞台裏のポインタである配列を、初期化した変数の内容が保存されているメモリ位置のアドレスに返しています。そのため、代わりに配列値の1つを実際に意味する場合に、そのような結果を返すことはそれほど有用ではない可能性があることを警告しています。

0
evandrix