式"str"
のタイプはchar[4]
です。(C++ では、 になりますconst char[4]
。)
配列型の式は、ほとんどのコンテキストで、配列オブジェクトの最初の要素へのポインターに暗黙的に変換されます。この変換は、一般に「減衰」と呼ばれます。これに対する例外は次のとおりです。
- 単項演算子のオペランドの場合
sizeof
(sizeof "str"
は、ポインターのサイズではなく、配列のサイズを生成します)。
- それが単項演算子のオペランドである場合
&
(ではなく&"str"
type の結果が得られます)。char(*)[4]
char*
- 配列オブジェクトの初期化に使用される初期化子の文字列リテラルの場合 (ここでは適用されません)。
これら 3 つのケースでは、配列式はその配列型を保持します。
文字列リテラルは、リテラルの文字と終端の'\0'
null 文字を保持するのに十分な大きさの、静的記憶域期間を持つ暗黙的に作成された配列オブジェクトを参照します。この場合、"str"
はタイプ の匿名静的オブジェクトを参照しますchar[4]
。
そう:
printf("%p\n", "str");
"str"
のをchar*
指す値に暗黙的に変換されます。's'
"str"
printf("%p\n", &"str"[0]);
上記のように、 "str"
in は に"str"[0]
減衰しchar*
ます。 を指す値を"str"[0"
生成します。したがって、 とは同じ型と値です。char*
's'
"str"
"str"[0]
printf("%p\n", &"str");
ここで、"str"
は のオペランドである&
ため、減衰は発生しないため&"str"
、匿名char[4]
オブジェクトの最初の文字のアドレスではなく、そのアドレスが生成されます。この式は、タイプchar(*)[4]
、または「4 文字の配列へのポインタ」です。
式&"str"[0]
と&"str"
両方がポインター値を生成します。どちらもメモリ内の同じ場所を指しますが、型は異なります。
3 つのケースすべてで、式の評価結果が引数として に渡されますprintf
。 printf
with "%p"
format には type の引数が必要void*
です。最初の 2 つのケースでは、 を渡しておりchar*
、 と に対する言語の要件は、それが期待どおりに機能することchar*
をvoid*
暗示しています。char*
3 番目のケースでは、 vs.にはそのようなルールがないchar(*)[4]
ため、 の動作は
printf("%p\n", &"str");
未定義です。
たまたま、ほとんどの実装では、すべてのポインター型が同じサイズと表現を持っているため、任意の型のポインターを with に渡しても問題ありませprintf
ん"%p"
。
void*
3 つのケースすべてで、未定義の動作を回避して、式を に明示的にキャストできます (おそらくそうすべきです) 。
printf("%p\n", (void*)"str");
printf("%p\n", (void*)&"str"[0]);
printf("%p\n", (void*)&"str");
質問の 2 番目の部分では、明確な問題を扱います。関数へのポインタについてです。ポインター型の式の規則は、配列型の規則と似ています。関数型 (関数名など) の式は、sizeof
(不正な) または&
(のオペランドである場合を除き、関数へのポインターに暗黙的に変換されます。関数のアドレスを生成します)。そのため、関数への*
orの適用&
はノーオペレーションのように機能します。では*func
、func
最初に関数へのポインターに減衰し、ポインターを*
逆参照し、結果が再び関数へのポインターに減衰します。では&func
、は減衰を抑制しますが、それ自体が生成&
する関数への同じポインターを生成します。func