web-dev-qa-db-ja.com

エラー:関数はローカル変数のアドレスを返します

私はCの初心者で、自分で学んでいます。次の関数を作成しています:

char *foo(int x){
     if(x < 0){
        char a[1000];
        char b = "blah";
        x = x - 1;
        char *c = foo(x);
        strcpy(a, b);
        strcat(a, c);
        return a;
      }
    blah ...
}

基本的に、追加された文字列を返そうとしていますが、次のエラーが表示されます。

「エラー:関数はローカル変数のアドレスを返します」、提案、これを修正する方法?

23
Hello World

ローカル変数の有効期間は、それが定義されているブロック内のみです。ローカル変数が定義されているブロックの外に制御が移動すると、変数のストレージは割り当てられなくなります(保証されません)。したがって、変数の有効期間領域外の変数のメモリアドレスを使用すると、未定義の動作になります。

一方、次のことができます。

 char *str_to_ret = malloc (sizeof (char) * required_size);
  .
  .
  .
 return str_to_ret;

そして、str_to_ret代わりに。 returning str_to_retmallocによって割り当てられたアドレスが返されます。 mallocによって割り当てられたメモリはヒープから割り当てられます。ヒープの有効期間はプログラムの実行全体に及びます。したがって、プログラムの実行中はいつでも任意のブロックからメモリの場所にアクセスできます。

また、割り当てられたメモリブロックを処理した後、freeを使用してメモリリークを防ぐことをお勧めします。メモリを解放すると、そのブロックに再びアクセスできなくなります。

41
phoxis

私は、それ自体を説明するべきであるこの単純で率直な(そう願っています)コード例を思いつきました!

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

/* function header definitions */
char* getString();                     //<- with malloc (good practice)
char * getStringNoMalloc();  //<- without malloc (fails! don't do this!)
void getStringCallByRef(char* reference); //<- callbyref (good practice)

/* the main */
int main(int argc, char*argv[]) {

    //######### calling with malloc
    char * a = getString();
    printf("MALLOC ### a = %s \n", a); 
    free(a);

    //######### calling without malloc
    char * b = getStringNoMalloc();
    printf("NO MALLOC ### b = %s \n", b); //this doesnt work, question to yourself: WHY?
    //HINT: the warning says that a local reference is returned. ??!
    //NO free here!

    //######### call-by-reference
    char c[100];
    getStringCallByRef(c);
    printf("CALLBYREF ### c = %s \n", c);

    return 0;
}

//WITH malloc
char* getString() {

    char * string;
    string = malloc(sizeof(char)*100);

    strcat(string, "bla");
    strcat(string, "/");
    strcat(string, "blub");

    printf("string : '%s'\n", string);

    return string;
}

//WITHOUT malloc (watch how it does not work this time)
char* getStringNoMalloc() {

     char string[100] = {};

     strcat(string, "bla");
     strcat(string, "/");
     strcat(string, "blub");
     //INSIDE this function "string" is OK
     printf("string : '%s'\n", string);

     return string; //but after returning.. it is NULL? :)
}

// ..and the call-by-reference way to do it (prefered)
void getStringCallByRef(char* reference) {

    strcat(reference, "bla");
    strcat(reference, "/");
    strcat(reference, "blub");
    //INSIDE this function "string" is OK
    printf("string : '%s'\n", reference);
    //OUTSIDE it is also OK because we hand over a reference defined in MAIN
    // and not defined in this scope (local), which is destroyed after the function finished
}

コンパイルすると、[意図した]警告が表示されます。

me@box:~$ gcc -o example.o example.c 
example.c: In function ‘getStringNoMalloc’:
example.c:58:16: warning: function returns address of local variable [-Wreturn-local-addr]
         return string; //but after returning.. it is NULL? :)
            ^~~~~~

...基本的にここで議論していること!

私の例を実行すると、次の出力が得られます。

me@box:~$ ./example.o 
string : 'bla/blub'
MALLOC ### a = bla/blub 
string : 'bla/blub'
NO MALLOC ### b = (null) 
string : 'bla/blub'
CALLBYREF ### c = bla/blub 

理論:

これは、ユーザー@phoxisによって非常にうまく回答されています。基本的にこのように考えてください:{}の間のすべてはlocalスコープなので、C-Standardでは「外で定義されていません。 mallocを使用すると、[〜#〜] heap [〜#〜](プログラムスコープ)からではなく、[〜#〜 ] stack [〜#〜](関数スコープ)-したがって、外部から「見える」。 2番目の正しい方法は、call-by-referenceです。ここでは、親スコープ内で変数を定義します。したがって、STACKを使用しています(親スコープはmain()であるため)。

概要:

それを行う3つの方法、そのうちの1つは間違っています。 Cは、関数が動的にサイズ設定されたStringを返すようにするだけでは不器用です。 mallocしてから解放するか、参照による呼び出しが必要です。または、C++を使用します;)

9
Gewure

Mallocも参照による呼び出しも必要ありません。関数内でポインターを宣言し、それを返す文字列/配列に設定できます。

@Gewureのコードを基礎として使用:

char *getStringNoMalloc(void){
    char string[100] = {};
    char *s_ptr = string;

    strcat(string, "bla");
    strcat(string, "/");
    strcat(string, "blub");
    //INSIDE this function "string" is OK
    printf("string : '%s'\n", string);

    return s_ptr; 
}

完全に動作します。

元の質問のコードの非ループバージョンでは:

char *foo(int x){    
    char a[1000];
    char *a_ptr = a;
    char *b = "blah";       

    strcpy(a, b);

    return a_ptr;
}
7
AuContraire

この行:

char b = "blah";

良くない-左辺値はポインタである必要があります。

再帰チェックはxの値の減少を制限しないため、コードもスタックオーバーフローの危険にさらされます。

とにかく、あなたが得ている実際のエラーメッセージは、char aは自動変数です。 returnが存在しなくなった瞬間。自動変数以外のものが必要です。

3
Brent Arias

aは関数に対してローカルな配列です。関数が返すと、それはもう存在しないので、ローカル変数のアドレスを返さないでください。
つまり、lifetimeaはスコープ内にあります(_{_、_}_)関数へのポインターを返す場合、持っているものは無効なメモリを指すポインターです。このような変数は、automatic variabelsとも呼ばれます。その寿命は自動的に管理されるため、明示的に管理する必要はありません。

関数のスコープを超えて持続するために変数を拡張する必要があるため、ヒープに配列を割り当て、その配列にポインターを返す必要があります。

_char *a = malloc(1000); 
_

このようにして、同じアドレスでfree()を呼び出すまで、配列aはメモリに常駐します。
忘れずにメモリリークが発生します。

2
Alok Save

aは関数内でローカルに定義されており、関数の外部では使用できません。関数からchar配列を返したい場合は、動的に割り当てる必要があります。

char *a = malloc(1000);

そして、ある時点で、返されたポインターでfreeを呼び出します。

次の行にも警告が表示されます:char b = "blah";charに文字列リテラルを割り当てようとしています。

1
pb2q