2017/02/07

[c/c++]未だに「配列のポインタ」「ポインタの配列」に怯える

愚痴だ。
こう何十年もC/C++を使っているにもかかわらず、「配列のポインタ」「ポインタの配列」が、どっちがどっちかわからなくなる。
ネットで説明が出てくるので、困ったときには調べれば良いのだが、どうも身になっていない。

 

ポインタの配列は使うことが多いと思う。
文字列の配列なんかは、ポインタの配列扱いしてよいだろう。

const char *STRINGS[] = {
  "Hello", "my", "name", "is", "Taro", "Hara"
};

あまり意識したことがないのだが、この「*」は、const charの方にひっついているはずだ。

for (int lp = 0; lp < sizeof(STRINGS) / sizeof(STRINGS[0]); lp++) {
    printf("%s\n", STRINGS[lp]);
}

これで全部の文字列が出力されたので、まあよかろう。

 

優先順位が心配だから括弧で強制してしまえ!、という技が、型については使えないのが残念だ。
これができれば、優先順位を覚えていなくてもよいのだが。。。コンパイルエラーだ。

(const char *)STRINGS[] = {
  "Hello", "my", "name", "is", "Taro", "Hara"
};

だから、逆に考えて、括弧を付けてコンパイルエラーになる方が優先順位が高い、と覚えれば良いのか。
つまり、「ポインタの配列」の方が優先順位が高いことになる。

 

ポインタの配列を関数の引数にしたいことがあるけど、ダブルポインタにキャストして渡している。
他に方法が思いつかないのよねぇ。


では、括弧を付けられる方を見ていこう。
こっちが「配列のポインタ」のはずだ。

const char (*STRINGS)[];

でも、配列って、そもそもポインタと同じ扱いなんじゃないのか?
なんでわざわざ「配列のポインタ」なんてことにしないといかんのだろう。

const char a1[] = "Hello";
const char (*STRINGS)[] = &a1;
printf("%s\n", *STRINGS);

一応、これで動く。
例として意味は無いので、何か有効な使い道を思いつけば良いのだが、使い道が思いつかないから普段使わないのだ。

 

でも、&a1って、a1と同じ値だよね?

printf(" a1 = %p\n", a1);
printf("&a1 = %p\n", &a1);
a1 = 0x7fffe8c5b100
&a1 = 0x7fffe8c5b100

まあ、配列はアドレスに展開されるから、それに&をつけても同じアドレスになりますわな。
だからといって、代入するときに&を取り外すとコンパイルエラーになる。
数字の問題じゃなくて、文法の問題だからだ。
レジスタのアドレス値が分かっていたとしても、ポインタとしてキャストして代入しないとエラーになるのと同じだ。


これは、なんだろう?

const char (*STRINGS[]);

gccだと「invalid initializer」になるので、文法エラーじゃなくて、初期化子の書き方が間違っている、といっているような気がするのだが。。。

const char (*STRINGS[]) = {
    "Hello", "my", "name", "is", "Taro", "Hara"
};

ちっ、そんだけかい。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。

注: コメントを投稿できるのは、このブログのメンバーだけです。