15

次のように C 文字列を返す単純な関数があるとします。

const char * getString()
{
  const char * ptr = "blah blah";
  return ptr; 
}

そして、次のように main() から getString() を呼び出します。

  const char * s = getString();

1) gdb によると、変数ptrはスタックに格納されますが、ptr が指す文字列はそうではありません。

(gdb) p &ptr
$1 = (const char **) 0x7fffffffe688

(gdb) p ptr
$2 = 0x4009fc "blah blah"

これは、「何とか」が getString() 内のローカル変数ではないということですか?

それがローカル変数である場合、それを main() 関数に渡すことができないと思います...しかし、そうでない場合、どこに保存されていますか? ヒープ上?それは、文字列にヒットするたびにOSによって実装される「一種の」動的メモリ割り当てですか、それとも何ですか?

2) ポインターの代わりに配列を使用する場合、次のようにします。

const char *getString2()
{
  const char a[] = "blah blah blah";
  return a;
}

コンパイラは次のように警告します。

warning: address of local variable ‘a’ returned

(もちろん、プログラムはコンパイルされますが、動作しません)。

実際、gdbに尋ねると、

(gdb) p &a
$2 = (const char (*)[15]) 0x7fffffffe690

しかし、const char * ptrconst char a[]は基本的に同じものだと思いました。そうではないようです。

私が間違っている?2 つのバージョンの正確な違いは何ですか?

ありがとうございました!

4

5 に答える 5

12

あなたが書くとき

const char *ptr = "blah blah";

次に、次のことが起こります。コンパイラはchar []、内容を含む (型の)定数文字列を生成し、実行可能ファイルのデータ セグメントのどこかに格納します (基本的に、キーワード"blah blah"を使用して宣言された変数の格納期間と同様の格納期間を持ちます)。static

次に、プログラムの存続期間を通じて有効なこの文字列のアドレスがポインターに格納され、ptrポインターが返されます。すべて良好。

"blah blah"これは getString() 内のローカル変数ではないということですか?

壊れた英語の文で答えさせてください。はい、そうではありません。

ただし、次のように配列を宣言すると、

const char a[] = "blah blah";

その場合、コンパイラは静的文字列を生成しません。(実際、これは文字列を初期化するときの特殊なケースです。) 次に、a 配列に十分な大きさのスタック メモリを割り当て(ポインターではありません!)、文字列のバイトを格納するコードを生成します。a これは実際にはローカル変数であり、そのアドレスを返すと、未定義の動作が発生します。

そう...

でも、基本的には同じだconst char *ptrと思っていました。const char a[]

いいえ、配列はポインタではないため、まったくありません。

于 2012-12-22T16:12:00.033 に答える
2

a[]あなたの関数では、 arrayのスコープは function 内にありますgetString2()。そのローカル配列変数

const char *getString2()
{
  const char a[] = "blah blah blah";
  return a;
}  

上記の場合、文字列の"blah blah blah"コピーが最初に実行され、ステートメントでa[]その配列を返そうとしていますがreturn a、定数文字列ではありません。

最初のコードのように、getString()ptrptr = "blah blah";はグローバル スコープを持つメモリを指します。

const char * getString()
{
  const char * ptr = "blah blah";
  return ptr; 
}

この場合、"blah blah"正当な定数文字列のアドレスを返します。

したがって、実際にはスコープの問題です。

の C プログラムのメモリ レイアウト変数のスコープについて学習すると役立ちますC

于 2012-12-22T16:07:43.580 に答える
2

それらが同じものではないという点で、あなたは正しいです。char a[] は、スタック上に形成された配列であり、「何とか..」で埋められます-関数内には、本質的に `const char a[15]; があります。strcpy(a, "何とか何とか");"

The const char *ptr = "blah blah blah";一方、単なるポインターであり(ポインター自体はスタック上にあります)、ポインターは文字列「何とか何とか」を指します。これは別の場所に保存されています[「読み取り専用データ」である可能性が最も高い]。

何かを変更しようとすると、大きな違いに気付くでしょう。たとえば、 a[2] = 'e';vs ptr[2] = 'e';- スタック値を変更しているため、最初のものは成功しますが、読み取り専用のメモリを変更しているため、2 番目 (おそらく) は失敗します。 、もちろん動作しないはずです。

于 2012-12-22T16:09:00.023 に答える
1

それらは同じではありません。

1 つ目は、文字列リテラルへのポインターです。ポインター自体は自動ストレージにあります。文字列は静的な読み取り専用メモリにあります。それは不変です。

2 つ目は、自動 (スタック)char配列です (警告にあるように、その戻り値は正当ではありません)。

于 2012-12-22T16:07:47.780 に答える