web-dev-qa-db-ja.com

Cでのポインターの印刷

ポインターで何かを理解しようとしていたので、このコードを書きました。

#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'へのポインタである場合、&schar **である必要がありますか?そして、なぜ他の警告が表示されるのですか?それらを印刷するために、ポインターを(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の値)を同じにすることができますか?

38
alcuadrado

「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;
25
James Curran

はい、コンパイラはvoid *を予期しています。 void *にキャストするだけです。

/* for instance... */
printf("The value of s is: %p\n", (void *) s);
printf("The direction of s is: %p\n", (void *) &s);
15
indiv

配列の名前を引数として関数に渡すと、配列のアドレスを渡したかのように扱われます。したがって、&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の電子書籍版)

4
Airsource Ltd

文字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

3
4pie0

行を変更:

char s [] = "asd";

に:

char * s = "asd";

そして物事はより明確になります

1

静的配列の値(つまり、アドレス)を変更することはできません。専門用語では、配列の左辺値はその最初の要素のアドレスです。したがって、s == &s。それは言語のちょっとした癖です。

1
Chris Conway

通常、不必要に(void *)にポインターをキャストするのはスタイルが悪いと考えられています。ただし、ここでは、printfは可変個であるため、printf引数の(void *)へのキャストが必要です。プロトタイプは、呼び出しサイトでポインターを変換する型をコンパイラーに通知しません。

1
fizzer

以下を使用しました:

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"を使用した場合、この追加の間接指定を使用できます。

0
selwyn