-1

次のコードで、何も表示されない理由は、get_message() によって返されたポインタが範囲外であるためです。

char *get_message() {
    char msg [] = "Aren’t pointers fun?";
    return msg ;
}

int main (void) {
    char *foo = get_message();
    puts(foo);
    return 0;
}

gdb で実行すると、foo の位置のデータが文字列 "Aren't pointers fun?" であることがわかります。

Old value = 0x0
New value = 0x7fffffffde60 "Aren’t pointers fun?"

(これは、スコープ外に渡されたポインターのデータがメモリに残るという回答と一致しているようです)が、「プット」のドキュメントには、最初のデータが指定されたアドレスからコピーされると記載されています。この場合、おそらく 0x7ffffffffde60 です。

したがって、何も出力されないのはなぜですか?

編集: ご回答ありがとうございます: 元のコードを gdb で完全に実行しました。puts の呼び出しは、foo が保存されたアドレスのデータを実際に変更します。

(gdb) p foo
$1 = 0x7fffffffde60 "Aren’t pointers fun?"
(gdb) n

11      return 0;
(gdb) p foo
$2 = 0x7fffffffde60 "`\336\377\377\377\177"

興味深いことに、change_msg() のコードを次のように変更すると、コードはメッセージを出力しました

char *get_message() {
        char *msg = "Aren’t pointers fun?";
    return msg ;
} 

この場合、foo のデータ (アドレス 0x4005f4 - アドレスのサイズが小さいことに何か意味があるのでしょうか?) は、コード全体で同じままです。これが動作を変更する理由を見つけるのは素晴らしいことです

4

5 に答える 5

4

変数 msg は get_message() のスタックに割り当てられます

char msg [] = "Aren’t pointers fun?";

get_message() が戻ると、そのメソッドのスタックは破棄されます。その時点で、返されたポインタがfoo現在指しているメモリの内容が保証されません。

puts() が呼び出されると、スタックが変更され、「Aren't pointer's fun」が上書きされる可能性があります。

于 2015-07-22T21:14:28.003 に答える
1

ここでの本当の問題は、「なぜうまくいかないのか?」ということではありません。問題は、「から戻った後も文字列が存在しているように見えるget_messageのに、それでも機能しないのはなぜですか?」ということです。

main明確にするために、参照用に 2 つのコメントを付けて、関数をもう一度見てみましょう。

int main (void) {
    char *foo = get_message();
    /* point A */
    puts(foo);
    /* point B */
    return 0;
}

これをgdbでコンパイルして実行しました。実際、 でpoint A、gdb の変数の値を出力するとfoo、gdb はそれが文字列 を指していることを示しました"Aren’t pointers fun?"。しかし、putsその文字列を印刷できませんでした。その後、 gdb でpoint B再度出力すると、元fooの文字列ではなくなりました。

いくつかの以前のコメンターが説明したように、その説明は、関数get_messageが文字列をスタックに残し、そこに長く留まることが保証されていないということです。返された後get_message、他の何かが呼び出される前に、それはまだそこにあります。しかし、 を呼び出して作業putsputs開始すると、スタックの同じ部分を独自のローカル ストレージに使用しているため、その場所で (puts実際に文字列を出力する前に) 文字列が破棄されます。


OPのフォローオンの質問に応えて:私たちが持っていたとき

char *get_message() {
    char msg [] = "Aren’t pointers fun?";
    return msg ;
}

文字列msgはスタック上の配列に存在し、その配列へのポインターを返しますが、配列内のデータが最終的に消えるため、これは機能しません。に変更すると

char * msg = "Aren’t pointers fun?";

(非常に小さな変更です!)、文字列はプログラムの初期化されたデータ セグメントに格納され、そのポインタを返しますこれはプログラムの初期化されたデータ セグメントにあるため、本質的に永久に残ります。(そして、そうです、get_message異なる外観のアドレスを返すことになるという事実は重要ですが、それが低いか高いかについてはあまり読みません.)


要するに、配列とポインターは異なるということです。とてつもなく違う。この線

char arr[] = "Hello, world!";

非常によく似たラインとはほとんど関係がありません

char *ptr = "Hello, world!";

両方できるという点では同じです

printf("%s\n", arr);

printf("%s\n", ptr);

でも言おうとすると

arr = "Goodbye";    /* WRONG */

配列に割り当てることができないため、できません。ここで新しい文字列が必要な場合は、 を使用strcpyする必要があり、新しい文字列が同じ長さか短いことを確認する必要があります。

strcpy(arr, "Goodbye");

strcpyしかし、ポインタで試してみると:

strcpy(ptr, "Goodbye");    /* WRONG */

ポイントする文字列定数は書き込み不可であるため、これは機能ません。ptrポインターの場合、単純な代入を使用できます (多くの場合、使用する必要があります)。

ptr = "Goodbye";

この場合、より長い文字列に設定しても問題ありません。

ptr = "Supercalafragalisticexpialadocious";

これらは基本的な違いですが、この質問が指摘しているように、もう1つの大きな違いは、配列arrを関数で便利に宣言して関数から返すことができないことです(あなたがそれを作成しない限りstatic)。ポインターptrは可能です。

于 2015-07-22T21:36:49.827 に答える
1

呼び出しputsによってスタックが変更され、文字列が上書きされる可能性があります。

から戻るだけでget_message、文字列は変更されませんが、割り当てが解除されます。つまり、そのメモリ空間は再利用できます。

于 2015-07-22T21:16:12.333 に答える