2

このトピックはすでに何度か議論されていることを知っており、基本的に配列とポインターの違いを知っていると思いますが、配列がメモリに正確に格納される方法に興味があります。

例えば:

const char **name = {{'a',0},{'b',0},{'c',0},0};
printf("Char: %c\n", name[0][0]); // This does not work

しかし、次のように宣言されている場合:

const char *name[] = {"a","b","c"};
printf("Char: %c\n", name[0][0]); // Works well

すべてがうまくいきます。

4

6 に答える 6

5

のような変数を定義すると

char const*  str = "abc";
char const** name = &str;

次のようになります。

+---+     +---+    +---+---+---+---+
| *-+---->| *-+--->| a | b | c | 0 |
+---+     +---+    +---+---+---+---+

フォームを使用して変数を定義する場合

char const* name[] = { "a", "b", "c" };

ポインターの配列があります。これは次のようになります。

          +---+     +---+---+
          | *-+---->| a | 0 |
          +---+     +---+---+
          | *-+---->| b | 0 |
          +---+     +---+---+
          | *-+---->| c | 0 |
          +---+     +---+---+

紛らわしいかもしれませんが、この配列をどこかに渡すと、ポインターに崩壊し、次のようになります。

+---+     +---+     +---+---+
| *-+---->| *-+---->| a | 0 |
+---+     +---+     +---+---+
          | *-+---->| b | 0 |
          +---+     +---+---+
          | *-+---->| c | 0 |
          +---+     +---+---+

つまり、配列の最初の要素へのポインターを取得します。このポインターをインクリメントすると、配列の次の要素に移動します。

于 2012-11-15T01:09:29.700 に答える
2

文字列リテラルは暗黙的に に変換されchar const*ます。

中括弧初期化子はそうではありません。

あなたの例には関係ありませんが、知っておく価値があります.C++03までは、古いCとの互換性のために文字列リテラルを暗黙的にchar*(no ) に変換することもできましたが、C++11では幸いなことに、この安全でない変換は最終的に削除されました.const

于 2012-11-15T00:42:16.180 に答える
1

最初の例では、char へのポインターへのポインターを宣言しています。2 番目は、char へのポインターの配列を宣言します。違いは、最初のレイヤーにもう 1 つの間接レイヤーがあることです。絵がないと説明が難しいです。

フェイク組み立てスタイルで、

 char **name = {{'a',0},{'b',0},{'c',0},0};

次のように変換されます。

t1:  .byte 'a', 0
  .align somewhere; possibly somewhere convenient
t2:  .byte 'b', 0
  .align
t3:  .byte 'c', 0
  .align
t4:  .dword t1, t2, t3, 0
name:  .dword t4

2つ目は

     char *name[] = {"a","b","c"};

t1、t2、および t3 に対して同じコードを生成する可能性がありますが、その場合は

name:  .dword t1, t2, t3

それは理にかなっていますか?

于 2012-11-15T00:53:41.770 に答える
1

変数を宣言したときに何が起こるか、変数のデータを格納するメモリがどこにあるのかを確認する必要があります。

まず、単純に書くとはどういう意味ですか:

char x = 42;

スタックに char を保持するのに十分なバイトが得られ、それらのバイトは値 42 に設定されます。

次に、配列を宣言するとどうなるか:

char x[] = "hello";

スタック上に 6 バイトが取得され、文字 h、e、l、l、o および値ゼロに設定されます。

文字ポインタを宣言するとどうなるか:

const char* x = "hello";

「hello」のバイトは静的メモリのどこかに保存され、スタックにポインタを保持するのに十分なバイトを取得し、その値は文字列の値を保持する静的メモリの最初のバイトのアドレスに設定されます。

では、2 番目の例のように宣言するとどうなるでしょうか。静的メモリに格納された 3 つの個別の文字列 "a"、"b"、および "c" を取得します。次に、スタック上で 3 つのポインターの配列を取得し、それぞれがこれら 3 つの文字列のメモリ位置に設定されます。

それで、あなたの最初の例は何をしようとしていますか? ポインターの配列へのポインターが必要なように見えますが、問題は、このポインターの配列がどこに行くのかということです。これは、静的メモリに何かを割り当てる必要がある上記のポインターの例のようなものです。ただし、そのようなブレースの初期化を使用して、静的メモリで 2 次元配列を宣言できないことがあります。したがって、配列を関数外の変数として宣言することで、必要なことを行うことができます。

const char* name_pointers[] = {"a", "b", "c"};

次に、関数内で:

const char** name = name_pointers;
于 2012-11-15T01:09:33.203 に答える
1

最初のスニペットが機能しない理由は、コンパイラが一連の文字をポインターの値として再解釈し、残りの初期化子を無視するためです。スニペットを機能させるには、次のように、配列を宣言していること、およびその配列の要素自体が配列であることをコンパイラに伝える必要があります。

const char *name[] = {(char[]){'a',0},(char[]){'b',0},(char[]){'c',0},0};

この変更を行うと、プログラムが機能し、目的の出力が生成されます ( ideone へのリンク)。

于 2012-11-15T00:46:09.297 に答える
1

配列は、オブジェクトの連続したシーケンスとしてメモリに格納されます。そのオブジェクトの型は、配列の基本型です。したがって、配列の場合:

const char *name[] = {"a","b","c"};

配列の基本型は で、配列const char *のサイズは 3 です (初期化子に 3 つの要素があるため)。メモリ内では次のようになります。

| const char * | const char * | const char * |

配列の要素はポインターであることに注意してください。実際の文字列は配列に格納されません。これらの文字列のそれぞれは文字列リテラルであり、の配列ですchar。この場合、それらはすべて 2 つの の配列でcharあるため、メモリ内の別の場所に 3 つの名前のない配列があります。

| 'a' |  0  |
| 'b' |  0  |
| 'c' |  0  |

初期化子は、配列の 3 つnameの要素を、これら 3 つの名前のない配列の初期要素を指すように設定します。 name[0]は を指し、 は'a'name[1]指し、 は'b'name[2]指し'c'ます。

于 2012-11-15T01:08:00.020 に答える