ポインタへのポインタが配列へのポインタと互換性がないのはなぜですか?

OK、ポインタへのポインタと配列へのポインタの理解に問題があります。 次のコードを考えてみましょう:

char s[] = "Hello, World";
char (*p1)[] = &s;
char **p2 = &s;
printf("%c\n", **p1); /* Works */
printf("%c\n", **p2); /* Segmentation fault */

なぜ、最初のprintfは動作しますが、2番目のprintfは動作しませんか?

私が理解しているところから、 's'は配列の最初の要素へのポインタです(つまり、 'H')。 したがって、p2をchar **として宣言すると、charへのポインタへのポインタであることを意味します。 's'はcharへのポインタなので、 's'をポイントすることは合法的でなければなりません。したがって、逆参照(** p2)すると 'H'が返されます。しかし、それはしません!

12
@Meta:GCC 4.3.4(デモ)または4.5.1(デモ)...
追加された 著者 ildjarn,
@メータ:ああ、あなたのコードは有効なCですが、有効なC ++ではありません。 c ++ タグは誰もが捨てていたので削除しました。
追加された 著者 ildjarn,
どの割り当てもVC ++ 2010でコンパイルされません。
追加された 著者 Jon,
奇妙な。 GCC 4.4.4では正常に動作します。
追加された 著者 Meta,

3 答え

あなたの誤解は s の内容にあります。 ポインタではありません:配列です。

Now in most contexts, s evaluates to a pointer to the first element of the array: equivalent to &s[0], a pointer to that 'H'. The important thing here though is that that pointer value you get when evaluating s is a temporary, ephemeral value - just like &s[0].

そのポインタは永続オブジェクトではないので(実際には s に格納されているものではありません)、ポインタポインタをポインタポイントにすることはできません。ポインタへのポインタを使用するには、実際のポインタオブジェクトをポイントする必要があります。たとえば、次のようにします。

char *p = s;
char **p2 = &p;

If you evaluate *p2, you're telling the compiler to load the thing that p2 points to and treat it as a pointer-to-char. That's fine when p2 does actually point at a pointer-to-char; but when you do char **p2 = &s;, the thing that p2 points to isn't a pointer at all - it's an array (in this case, it's a block of 13 chars).

11
追加された
&s はコンパイルエラーではない(ただし、&(a + 1)はどうですか) ?
追加された 著者 Shahbaz,
@カフェ、それは意味をなさない始めている。配列名は関数名と似ていますが、はそのアドレスを示しますが、また、アドレスを与える!同じ方法 func &func は同じ値になります。これは char(* p)[] のように char(* p)[] を使用する理由です。私は正しいですか?
追加された 著者 Shahbaz,
@caf、それを得た;)
追加された 著者 Shahbaz,
@Meta: s が評価するポインタは、 a + 1 と同じ方法でアドレス指定可能なオブジェクトではないことを意味します(standardeseでは、 )。
追加された 著者 caf,
@Shahbaz: s は、アドレス指定可能なオブジェクトです。これは配列ですが、ほとんどのコンテキストで s i>はアドレス指定可能なオブジェクトではありません。 </code>は最初の要素へのポインタを評価する ことはありません。 array &s はその配列のアドレスを返します。 s がまだ配列自体を指定している他のコンテキストは sizeof s です(これはポインタではなく配列のサイズです)。
追加された 著者 caf,
@ Shahbaz:それは機能との状況に非常に似ていますが、同一ではありません。違いは、&func のような型と値を持つ関数へのポインタを func のような関数指定子で評価することです。配列の場合、 s のような配列名は配列の最初の要素へのポインタに評価されますが、&s i>配列に移動します。配列の最初の要素は必ず配列の先頭にあるので、 s &s は同じ値を持ちます(例えば、 )、それらは異なるタイプです。
追加された 著者 caf,
ああ。私は今理解し始めていると思う。あなたは「一時的な、一時的な価値」であることについての部分だけを取り除くことができますか?それはいつも同じアドレスではないでしょうか?
追加された 著者 Meta,

From what I understand, 's' is a pointer to the first element of the array
No, s is an array. It can be reduced to a pointer to an array, but until such time, it is an array. A pointer to an array becomes a pointer to the first element of the array. (yeah, it's kinda confusing.)

char (*p1)[] = &s; This is allowed, it's a pointer to an array, assigned the address of an array. It points to the first element of s.

char **p2 = &s;
That makes a pointer to a pointer and assigns it the address of the array. You assign it a pointer to the first element of s (a char), when it thinks it's a pointer to a pointer to one or more chars. Dereferencing this is undefined behavior. (segfault in your case)

それらが異なるという証拠は、 sizeof(char [1000])にあります(ポインタのサイズではなく、1000文字のサイズを返します)。

template
void function(char (&arr)[length]) {}

配列は与えられたときにコンパイルされますが、ポインタはコンパイルされません。

1
追加された

ここでは動作するサンプルとポインタアドレスのプリントアウトがあり、物事を見やすくしています:

#include 
char s[] = "Hello, World";
char (*p1)[] = &s;
char *p2 = (char*)&s;

int main(void)
{
   printf("%x %x %x\n", s, p2, *p2);
   printf("%x\n", &s);   //Note that `s` and `&s` give the same value
   printf("%x\n", &s[0]);
   printf("%c\n", **p1); 
   printf("%c\n", *p2);
}
1
追加された
あなたのコードに1行追加して、なぜこれが起こるのかを少し明確にします。
追加された 著者 Shahbaz,