ポインターで何かを理解しようとしていたので、このコードを書きました。
#include <stdio.h>
int main(void)
{
char s[] = "asd";
char **p = &s;
printf("The value of s is: %p\n", s);
printf("The direction of s is: %p\n", &s);
printf("The value of p is: %p\n", p);
printf("The direction of p is: %p\n", &p);
printf("The direction of s[0] is: %p\n", &s[0]);
printf("The direction of s[1] is: %p\n", &s[1]);
printf("The direction of s[2] is: %p\n", &s[2]);
return 0;
}
Gccでコンパイルすると、次の警告が表示されます。
$ gcc main.c -o main-bin -ansi -pedantic -Wall -lm
main.c: In function ‘main’:
main.c:6: warning: initialization from incompatible pointer type
main.c:9: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char (*)[4]’
main.c:11: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char **’
main.c:12: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char ***’
(gccのフラグは、私がC89でなければならないためです)
なぜ互換性のないタイプのポインターですか?配列の名前は、その最初の要素へのポインターではありませんか? sが 'a'へのポインタである場合、&s
はchar **
である必要がありますか?そして、なぜ他の警告が表示されるのですか?それらを印刷するために、ポインターを(void *
)でキャストする必要がありますか?
そして、実行すると次のようなものが得られます:
$ ./main-bin
The value of s is: 0xbfb7c860
The direction of s is: 0xbfb7c860
The value of p is: 0xbfb7c860
The direction of p is: 0xbfb7c85c
The direction of s[0] is: 0xbfb7c860
The direction of s[1] is: 0xbfb7c861
The direction of s[2] is: 0xbfb7c862
Sの値とその方向(そしてもちろんp
の値)を同じにすることができますか?
「s」は「char *」ではなく、「char [4]」です。したがって、「&s」は「char **」ではなく、実際には「4文字の配列へのポインター」です。コンパイラは、 "&s [0]"を記述したかのように "&s"を扱います。これは、ほぼ同じことですが、 "char *"です。
「char ** p =&s;」と書くと「現在、「asd」を指しているもののアドレスにpを設定したいのですが、現在はpoints to「asd」というものはありません。ただ配列があります。 which holds "asd";
char s[] = "asd";
char *p = &s[0]; // alternately you could use the shorthand char*p = s;
char **pp = &p;
はい、コンパイラはvoid *を予期しています。 void *にキャストするだけです。
/* for instance... */
printf("The value of s is: %p\n", (void *) s);
printf("The direction of s is: %p\n", (void *) &s);
配列の名前を引数として関数に渡すと、配列のアドレスを渡したかのように扱われます。したがって、&sとsは同一の引数です。 K&R 5.3を参照してください。 &s [0]は、配列の最初の要素のアドレスを取得するため、&sと同じです。これは、配列自体のアドレスを取得するのと同じです。
他のすべての場合、すべてのポインターは本質的にメモリ位置であるにもかかわらず、まだ入力されており、コンパイラーは、あるタイプのポインターを別のタイプに割り当てることについて警告します。
void* p;
はpがメモリアドレスであると言いますが、メモリに何が入っているのかわかりませんchar* s;
は、sがメモリアドレスであり、最初のバイトに文字が含まれていることを示しますchar** ps;
は、psがメモリアドレスであり、そこの4バイト(32ビットシステムの場合)にはchar *型のポインターが含まれていると言います。cf http://www.oberon2005.ru/paper/kr_c.pdf (K&Rの電子書籍版)
文字char*
へのポインターではなく、4文字の配列char* [4]
へのポインターです。 g ++ではコンパイルされません。
main.cpp:関数「int main(int、char **)」:main.cpp:126:エラー:初期化で「char(*)[4]」を「char **」に変換できません
さらに、Linuxのmanページ says :
p
Void *ポインター引数は16進数で出力されます(%#xまたは%#lxのように)。 voidへのポインターである必要があります。
コードを次のように変更できます。
char* s = "asd";
char** p = &s;
printf("The value of s is: %p\n", s);
printf("The address of s is: %p\n", &s);
printf("The value of p is: %p\n", p);
printf("The address of p is: %p\n", &p);
printf("The address of s[0] is: %p\n", &s[0]);
printf("The address of s[1] is: %p\n", &s[1]);
printf("The address of s[2] is: %p\n", &s[2]);
結果:
Sの値:0x403f00
Sのアドレス:0x7fff2df9d588
Pの値:0x7fff2df9d588
Pのアドレス:0x7fff2df9d580
S [0]のアドレス:0x403f00
S [1]のアドレスは次のとおりです:0x403f01
S [2]のアドレスは次のとおりです:0x403f02
行を変更:
char s [] = "asd";
に:
char * s = "asd";
そして物事はより明確になります
静的配列の値(つまり、アドレス)を変更することはできません。専門用語では、配列の左辺値はその最初の要素のアドレスです。したがって、s == &s
。それは言語のちょっとした癖です。
通常、不必要に(void *)にポインターをキャストするのはスタイルが悪いと考えられています。ただし、ここでは、printfは可変個であるため、printf引数の(void *)へのキャストが必要です。プロトタイプは、呼び出しサイトでポインターを変換する型をコンパイラーに通知しません。
以下を使用しました:
char s[] = "asd";
ここで、sは実際には「asd」バイトを指しています。 sの住所もこの場所を指します。
使用した場合:
char *s = "asd";
sは実際にはバイト「asd」へのポインタであるため、sと&sの値は異なります。
使いました:
char s[] = "asd";
char **p = &s;
ここで、sは「asd」バイトを指します。 pは文字へのポインターへのポインターであり、文字のアドレスに設定されています。言い換えれば、pのインダイレクションが多すぎます。 char * s = "asd"を使用した場合、この追加の間接指定を使用できます。