ページ 11

文字列の代入されている場所について

Posted: 2010年9月11日(土) 14:08
by mol
こんにちは。
文字列について、「あいうえお」等の全角文字を配列に代入したい時、この場合5文字なので
char a[11];のように少なくとも11バイトのメモリを確保しなくてはならないのですよね?
そうすると、例えば「あ」はa[0]とa[1]に代入されることになりますか?
そうなった時、後で単発に「あ」を取り出したいとき、どのように処理をすればよいのでしょうか。
(例えば、3つ目の文字が「う」であるかどうかの判定は、「う」がa[4]とa[5]に代入されているため、a[4]=='う';とするのですか?)

Re:文字列の代入されている場所について

Posted: 2010年9月11日(土) 15:36
by たかぎ
処理系不明の状況では何ともいえません。
char型に 'あ' とか 'い' とかを格納することができる場合もあれば、8バイトほど必要になることもあります。

Re:文字列の代入されている場所について

Posted: 2010年9月11日(土) 15:52
by へろりくしょん
この手の話は、文字コードに依存しますので、勝手にShift-JISを前提とします。
ついでに、char 型が1バイトであることを前提とします。

>文字列について、「あいうえお」等の全角文字を配列に代入したい時、この場合5文字なので
>char a[11];のように少なくとも11バイトのメモリを確保しなくてはならないのですよね?

ですね。

>そうすると、例えば「あ」はa[0]とa[1]に代入されることになりますか?

そうなります。

>そうなった時、後で単発に「あ」を取り出したいとき、どのように処理をすればよいのでしょうか。
2バイト長のサイズを持つ型 type を使い

*(type*)&a[0]; という風にすれば可能です。

>(例えば、3つ目の文字が「う」であるかどうかの判定は、「う」がa[4]とa[5]に代入されているため、a[4]=='う';とするのですか?)

if(a[4] == "う"[0] && a[5] == "う"[1]) とするか
if(*(type*)&a[4] == *(type*)"う") とするか
if(!strncmp(&a[4], "う", 2)) とでもします。


type は環境に合わせて読み替えてください。

Re:文字列の代入されている場所について

Posted: 2010年9月11日(土) 16:03
by シエル
昔私がもがき苦しんでいたときのスレがありましたので、貼っておきます。
参考になれば幸いです。逆にこんごらがってしまったらすいません。。。

http://www.play21.jp/board/formz.cgi?ac ... &rln=56103

Re:文字列の代入されている場所について

Posted: 2010年9月12日(日) 00:54
by mol
たかぎさん>>なるほど、考えていませんでした。ええと、文字コードというのはパソコンごとに決まっているものなのですか?逆にプログラムによってこのプログラムはShift-JISを使用すると宣言できるものなのでしょうか?

へろりさん>>丁寧にありがとうございます。比較の例、非常にためになりました!
"う"[0]で「う」の前半の文字コードを示しているということでよろしいですか?
しかし、*(type*)&a[0]; とはどういった意味(文法?)でしょうか?

シエルさん>>自分の知識レベルを完全に超えていました。折角なのに申し訳ないです。

Re:文字列の代入されている場所について

Posted: 2010年9月12日(日) 00:55
by シエル
し、失礼致しました。。。

Re:文字列の代入されている場所について

Posted: 2010年9月12日(日) 06:57
by へろりくしょん
>"う"[0]で「う」の前半の文字コードを示しているということでよろしいですか?

言わんとするところは解りますので、その通りです。 と言いたいところですが。
言い切ってしまうと識者諸氏からの突っ込みを受けますので、重隅を承知で訂正させていただきます。

文字コードとはあくまでもShift-JISや、EUC-JP、Unicode等の文字を表現するビットパターンの体系の事ですので、正確には、"う"[0]は"う"の上位バイト。 ということになります。


>しかし、*(type*)&a[0]; とはどういった意味(文法?)でしょうか?
a                変数 a の
a[0]             [0]番目の要素の
&a[0]            ポインタを
(type*)&a[0]     (type*)型でキャストして
*(type*)&a[0]    で、アドレスを解決。
です。

Re:文字列の代入されている場所について

Posted: 2010年9月12日(日) 11:59
by mol
シエルさん>>半分理解しました。しかしまだ一つ疑問があります。

Re:文字列の代入されている場所について

Posted: 2010年9月12日(日) 12:09
by mol
申し訳ないです。上記のはミスです。
すいません、当初の質問からズレてしまいますが、(それ以前の知識がありませんでした。)

例えばこの場合、(文字コードはSift-JIS、char型が一バイトととして、type型が二バイトとします)
*(type*)&a[0]の意味は、前半は(一バイト目)もともとのa[0]のポインタを示していて、後半は(二バイト目)a[1]のポインタを指しているのですか?

つまりキャストする前よりキャスト後の方がメモリが大きいとき、自動的に読み込まれるのはメモリの次のアドレスに入っている要素なのでしょうか?

Re:文字列の代入されている場所について

Posted: 2010年9月12日(日) 13:10
by へろりくしょん
>つまりキャストする前よりキャスト後の方がメモリが大きいとき、自動的に読み込まれるのはメモリの次のアドレスに入っている要素なのでしょうか?

言いたいことが今ひとつ分かりません。

ポインタというのは要するに、ただ指し示す者。 それだけです。 実際のデータが格納されている場所(アドレス)を保持します。 それだけです。

例えば次のようなメモリマップがあったとします。
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
    ┏━━━━━━━━━━━━━━━━━━━━━━━━
0010┃00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0020┃00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
そこで
char a[5] = {0x01, 0x02, 0x03, 0x04, 0x05};
とします。

この時、変数 aの先頭要素(a[0])へのポインタ(&a[0])が 0015 の値を持つ時、
メモリには次のように書き込まれます。

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
┏━━━━━━━━━━━━━━━━━━━━━━━━
0010┃00 00 00 00 00 01 02 03 04 05 00 00 00 00 00 00
0020┃00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[/pre]
a[0] はアドレス 0015 に書き込まれており、その値は 0x01 です。
a[1] はアドレス 0016 に書き込まれており、その値は 0x02 です。
a[2] はアドレス 0017 に書き込まれており、その値は 0x03 です。

ここで、2バイト長の幅を持つ型 type を使い、
type* baa = (type*)&a[0]; とポインタ同士をキャストします。
ポインタは指し示す者で、その値はアドレスですから、この時の変数 baa の値は 0015 となりますね。
0015 の場所を指し示しています。

では、*baa と間接演算子を使いアドレスを解決した時の値は何になるかと言うと、変数 baa の型は type で、型 type は2バイト長な訳ですから、0x0102 となりますね。 ただそれだけの話です。


baa と &a[0] は同じ値で、baa と (type*)&a[0] は同じ意味で、*baa と、*(type*)&a[0] もやっぱり、全く同じ意味になります。

Re:文字列の代入されている場所について

Posted: 2010年9月12日(日) 15:05
by mol
へろりさん>>丁寧に本当にありがとうございます。

> ここで、2バイト長の幅を持つ型 type を使い、
> type* baa = (type*)&a[0]; とポインタ同士をキャストします。
> ポインタは指し示す者で、その値はアドレスですから、この時の変数 baa の値は 0015 となりますね。
> 0015 の場所を指し示しています。

> baa と &a[0] は同じ値で、baa と (type*)&a[0] は同じ意味で、*baa と、*(type*)&a[0] もやっぱり、全く同じ意味になります。

ここが疑問なのですが、(&a[0]が 0015 の値を持っていて)キャストした後の(type*)&a[0]もアドレスが変わらないのですか?つまり妙な書き方ですが(&a[0]==(type*)&a[0])=1ですか?(ではキャストする効果はどこに現れているのでしょう?)

そうすると、
> では、*baa と間接演算子を使いアドレスを解決した時の値は何になるかと言うと、変数 baa の型は type で、型 type は2バイト長な訳ですから、0x0102 となりますね。 
とありますが、 上記の話でbaa=(type*)&a[0]=&a[0]なので、間接参照すると*baa==*(type*)&a[0]=*(&a[0])となってしまいそうですが・・・(なるわけがない事は分かっています。) 

(もしかして、「(type*)&a[0]」というのは「二バイト超の値のポインタの先頭部分」という意味になるのですか?)

Re:文字列の代入されている場所について

Posted: 2010年9月12日(日) 17:07
by アビゲイル
なんとなくリトルエンディアンの問題な気がします。

Re:文字列の代入されている場所について

Posted: 2010年9月12日(日) 18:54
by たかぎ
> 文字コードというのはパソコンごとに決まっているものなのですか?

いいえ。

> 逆にプログラムによってこのプログラムはShift-JISを使用すると宣言できるものなのでしょうか?

それも厳密にいうと違います。

結局のところ、処理系や利用状況が不明のままでは、想像に次ぐ想像で回答するしかなく、まともな話にはなりません。
もっとはっきりいえば、質問する前に利用規約をちゃんと読み、それを守りましょう、ということです。

Re:文字列の代入されている場所について

Posted: 2010年9月12日(日) 19:04
by たかぎ
一応、処理系不明のままで回答すれば...

> 文字列について、「あいうえお」等の全角文字を配列に代入したい時、この場合5文字なので
> char a[11];のように少なくとも11バイトのメモリを確保しなくてはならないのですよね?

特殊な状況ではそうなりますが、一般的には違います。

char a[MB_CUR_MAX * 5 + 1];

とする必要があります。
ただし、旧規格にしか対応していない処理系であれば、MB_CUR_MAXの代わりにMB_LEN_MAXを使うか、mallocで動的に配列を割り付けるなどの方法をとらなければなりません。

> そうすると、例えば「あ」はa[0]とa[1]に代入されることになりますか?

特殊な状況ではそうなりますが、一般的には違います。
最大で、a[0] ~ a[MB_CUR_MAX - 1] の範囲に格納されることになります。
もちろん、これより短い可能性はあります。

> そうなった時、後で単発に「あ」を取り出したいとき、どのように処理をすればよいのでしょうか。

適切にロケールを設定した上で...

mbstate_t state = {0};
size_t length = mbrlen(a, MB_CUR_MAX, &state);

char b[MB_CUR_MAX];
memcpy(b, a, length);

とすれば、b[0]~b[length-1]の範囲に「あ」に相当する部分の値が取り出せます。
画像

Re:文字列の代入されている場所について

Posted: 2010年9月14日(火) 13:06
by mol
たかぎさん>>申し訳ないです。この関係の話が開発環境に依存するとは知らなかったので…
(ちなみに一応開発環境はwindowsxp,コンパイラがborlandC++で開発環境がBCCDeveloperでした。)
MB_CUR_MAXを用いる方法は全く知りませんでした。
丁寧にありがとうございました。