はじめに.文字列の配列
CとC++が混ざった環境で作業することがあったのだが、C++が使えなくなってしまった。そのため、string [] 型が使えなくなってしまった。
仕方ないので、Cでstring[]型の変数を書き直さなおすことになったのが事の発端。
string[]とはつまり、C++で書くとこんなコードのこと
/* C++ */ string arrStr[] = { "1", "12", "123", "1234", }; printf("%d\n", sizeof(arrStr)); //112
これをCで書き直す。
さて.文字(Char)へのポインタの配列
最初はふつうに変えればいいんだなと思っていたのだが、いざ書いてみようとすると、あれ、どう書くんだとなった。
いろいろ試行錯誤した結果、以下の結論に落ち着いた。
/* C言語 */ const char * arrPtChr [] = { "1", "12", "123", "1234", }; printf("%d\n", sizeof(arrPtChr)); //16
ぱっと見わかるだろうか?
(const char)へのポインタの配列だ。
いちど理解してみるとなるほど納得できる。
そもそも「string型の変数strの実体は、stringオブジェクトへのポインタである」ということを考えると、string[]をCで書き換えるには、「stringへのポインタ」を「const charへのポインタ」に置き換えればいいというわけだ。
余談
参照先の文字列の保証
参照先の文字列の値が変わらないことの保証がされているのか不安になったので、その確認もしてみた。
アセンブリを覗いてみると、文字列リテラル値がきちんと読み取り専用領域に配置されていることが分かる。
.section .rdata,"dr" LC0: .ascii "1\0" LC1: .ascii "12\0" LC2: .ascii "123\0" LC3: .ascii "1234\0"
そのため他の処理から参照先の値が書き換えられてしまう危険があるのではないかと恐れていたのだが、問題なさそうだ。
めでたしめでたし。
さらに余談
上では触れなかったが以下の宣言方法も考えた。
余談.文字配列の配列
誰もが一度は考える、配列の配列。
配列サイズの指定が必要となるのでまあ、却下。
const char arrArrChr[][5] = { "1", "12", "123", "1234", }; printf("%d\n", sizeof(arrArrChr)); //20
+α.文字配列へのポインタの配列
これがstring配列に一番近い気がする。
const char (*arrPtArrChr[]) [] = { &"1", &"12", &"123", &"1234", }; printf("%d\n", sizeof(arrPtArrChr)); //16
わかりづらいが。
『「{(const char)の配列}へのポインタ」の配列』だ。
わからない場合は参考文献を読み漁ってみてほしい。
ちなみに文字列リテラルの頭の「&」だが、つけないと以下のように怒られる。
warning: initialization of ‘const char (*)[]’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types]
ざっと和訳すると、『不適合なポインタ型 ‘char *’ で、 ‘const char (*)[]’の初期化をしている』となる。
なるほど、言われてみれば確かにその通りだ。「&」をつけないとポインタの型が違ってしまう。
もう一つ、sizeofで文字配列のサイズを求めてみたかったのだが、エラーになってしまった。
//エラー:char[]は不完全な型のため、sizeofが使えない。 printf("%d\n", sizeof(*arrPtArrChr[i]));
ざんねん。
以下.参考文献
C言語にはそんな規約があったのかと、大いに自分の勉強不足に気づいた。
読んでいていろんな謎が解けるので面白い。興味のある方はどうぞ。
「配列へのポインタ」と「ポインタの配列」の見分け方
配列とポインタの暗黙のキャストの話

constとポインタの位置によるコンパイラの解釈

変数の値の保証(書き換え不可の確認)

stringの文字列はどこにあるのかという話。以下はJava。C++ではよく知らないがたぶん同じなんじゃないかと勝手に想像。
コメント