49

私は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 ...
}

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

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

4

8 に答える 8

68

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

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

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

代わりに を使用しstr_to_retます。そしてreturningするとstr_to_ret、 によって割り当てられたアドレスmallocが返されます。によって割り当てられたメモリmallocは、プログラムの実行全体にわたる寿命を持つヒープから割り当てられます。したがって、プログラムの実行中はいつでも、任意のブロックからメモリ位置にアクセスできます。

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

于 2012-09-12T03:23:34.313 に答える
19

私はこのシンプルでわかりやすい (そうであってほしい) コード例を思いつきました。

#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 によって非常にうまく回答されています。基本的に次のように考えてください: {}の間のすべてがローカルスコープであるため、C 標準では外部は「未定義」です。malloc を使用すると、スタック (関数スコープ) からではなく、ヒープ (プログラム スコープ) からメモリを取得するため外部から「可視」になります。それを行う 2 番目の正しい方法はcall-by-referenceです。ここでは、親スコープ内で var を定義しているため、STACK を使用しています (親スコープがmain()であるため)。

概要:

それを行う3つの方法、そのうちの1つは偽です。C は、関数が動的にサイズ変更された文字列を返すようにするのはちょっと不器用です。malloc してから解放するか、参照渡しにする必要があります。またはC++を使用してください;)

于 2017-02-22T13:06:35.293 に答える
3

a関数にローカルな配列です。関数が返されると、それはもう存在しないため、ローカル変数のアドレスを返すべきではありません。
言い換えれば、の寿命は関数aの scope( {, }) 内にあり、それへのポインターを返す場合、有効ではないメモリを指すポインターになります。このような変数は、有効期間が自動的に管理され、明示的に管理する必要がないため、 自動変数とも呼ばれます。

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

char *a = malloc(1000); 

このようにして、同じアドレスでaa を呼び出すまで、配列はメモリ内に存在します。 そうしないと、メモリ リークが発生します。free()

于 2012-09-12T03:23:13.490 に答える
3

この行:

char b = "blah";

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

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

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

于 2012-09-12T03:24:13.540 に答える
1

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

char *a = malloc(1000);

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

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

于 2012-09-12T03:22:19.513 に答える
0
char b = "blah"; 

次のようにする必要があります。

char *b = "blah"; 
于 2016-08-08T23:30:08.373 に答える