【C言語】好きなサイズの文字列の配列を宣言する

スポンサーリンク

はじめに.文字列の配列

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言語にはそんな規約があったのかと、大いに自分の勉強不足に気づいた。
読んでいていろんな謎が解けるので面白い。興味のある方はどうぞ。

「配列へのポインタ」と「ポインタの配列」の見分け方

「配列へのポインタ」と「ポインタの配列」の見分け方 - めもめも
はい。どちらでしょう。 int (*hoge) 答えは、配列へのポインタなのですが、(私を含めて・・・)どうしても、ポインタの配列に見えて仕方がない人は、次の手順で構文解析してください。まず、丸カッコでポインタ記号(*)がhogeに縛り付けられているので、「ポインタ」が最後の述語になります。「hoge は (punyo...

 

配列とポインタの暗黙のキャストの話

【C言語】&配列名の値とは?
【C言語】&配列名の値とは? char arr[9]; arr, &arr, &arr[0], それぞれの値とは? 答えは、すべて同じ値で、配列の先頭アドレスになります。 理由は、C言語の規約にありました。 配列はその先頭要素へのポインタへ暗黙の型変換をされる ただし、この暗黙の型変換には例外があり、 & を適用するときは起こらない
ポインタと配列の微妙な関係 - めもめも
話の背景 C言語のポインタと配列の関係については、『「配列へのポインタ」と「ポインタの配列」の見分け方』で紹介した「エキスパートCプログラミング」という書籍の「徹底的な解説」をこよなく愛していたのですが、最近、紹介されて「C言語ポインタ完全制覇」という書籍を読んだところ、類似の内容が、もう一段噛み砕いた形で説明されてお...

 

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

図解:constとポインタと参照 - Qiita
本記事は「ポインタ/参照とconstキーワードについて、なんとなく分かってきたかも?」という学習ステージの方が、const修飾の役割を イメージできる ような理解を目指しています。図解(凡例)i…

 

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

C言語と文字列に関するあれこれ - Qiita
はじめにちょっと前にこんなコードが話題になりました(オリジナルから少し変更してあります)。 char str1 = "hoge "; char str2 = "fuga "; char…

 

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

Java Language Tutorial => String pool and heap storage
Learn Java Language - String pool and heap storage

 

コメント

タイトルとURLをコピーしました