私はこの質問に出くわしました:
#include<stdio.h>
int main()
{
char str[25]="Catch me, if u can!";
printf("%s\n",&str+2);
return 0;
}
の意味を説明できる人はい&str+2
ますか? これはどのように作動しますか?
&str
型のポインタを返しますchar (*)[25]
。つまり、このポインタが指すメモリのサイズはsizeof(str)
、つまり 25 になります。通常のポインタ演算では、この値に 2 を加算すると、配列char (*)[25]
の末尾から 25 バイト先を指すタイプのポインタになります。str
それを印刷することは、おそらく未定義の動作です。
str
型が「25文字の配列」の式です。ほとんどの場合、配列は最初の要素へのポインターに自動的に変換されます。したがって、 instr+2
は配列str
の最初のポインタに変換され、char
2 を加算すると、結果はchar
配列の 3 番目のポインタになります。
ただし、C 2011 6.3.2.1 3 によると、配列型の式が単項演算子sizeof
,のオペランドである場合、または配列の初期化に使用される文字列リテラルである場合、この自動変換は行われません。したがって、 inはポインターに変換されません。25 文字の配列のままです。次に、25 文字の配列へのポインターです。これに 2 を加算すると、概念的には、結果は 25 文字の配列の配列の 3 番目の要素へのポインターになります。_Alignof
&
&str+2
str
&str
ただし、ポインター演算は、配列の末尾にある架空の要素まで、配列内でのみ定義されます。(さらに、ポインター演算の目的で、単一のオブジェクトは 1 つの要素を持つ配列として扱われます。) 25 文字の配列の配列ではなく、25 文字の配列が 1 つしかないため、ポイントする 3 番目の要素はありません。であり、&str+2
未定義です。
まず第一に、あなたがそこで行っていることは、せいぜい未定義の動作になります。
str
文字列が始まるスタック上のメモリアドレスに関連するシンボルです。&str
はスタック シンボルでは無効であり、これを使用すると未定義の動作が発生します。おそらく必要なのはstr + 2
、これが文字列の 3 番目のバイトへのポインタであることです。
printf("%s\n",str+2);
印刷します"tch me, if u can!\n"
配列を定義する場合:
char str[25] = "Catch me, if you can!";
あなたが得ているのは、スタック上に予約された25バイトの空間で、次のものが含まれています。
@Address @Address
0xbfa7fc13 0xbfa7fc2C
| |
V V
[C][a][t][c][h][ ][m][e][,][ ][i][f][ ][y][o][u][ ][c][a][n][!][\0][\0][\0][\0]
strのアドレスを見る:
printf("%#x, %#x\n", str, &str);
0xbfa7fc13
、が表示されます0xbfa7fc13
。配列はそのアドレスから始まるため、同じ値です。str に 2 を追加すると、文字列のさらに下を指すことになります。
0xbfa7fc13 + 2 =
0xbfa7fc15
|
V
[t][c][h][ ][m][e][,][ ][i][f][ ][y][o][u][ ][c][a][n][!][\0][\0][\0][\0]
だから印刷:
printf("%s\n", str+2);
文字列をもう少し下にダンプします。ただし、 のアドレスに 2 を追加すると、追加するstr
もののサイズによって自動的に * それが行われます。
&str + 2 ==> &str + 2 * sizeof(str) ==> &str + 50 ==> 0xbfa7fc45
ご覧のとおり、アドレスは配列がスタック上にある場所をはるかに超えているため、次のように出力します。
printf("%s\n",&str+2);
'\0'
運が良ければ、a が見つかるまで、配列を 50 バイト過ぎたゴミを吐き出します。これは未定義の動作なので、ほとんど何でもできます。