プロが教えるわが家の防犯対策術!

環境:HP-UX11
文字:S-JIS

文字だろうと、10進数だろうと、メモリ上は 0 と 1 の羅列である!
という命題の実験をしようとしたところ、想定外の結果が出力されました。。
なぜこのような結果となるのかを教えてください。

#include <stdio.h>
#include <string.h>

int main()
{
char arr_c[6] = {'m','o','r','i','n','\0'};
int* ip;

ip = arr_c;

printf("arr_c :[%d]\n", arr_c ); /* [アドレス1]が出る */
printf("ip :[%d]\n", ip ); /* [アドレス1]が出る */
printf("*ip :[%c]\n", *ip ); /* [m]が出る */
printf("*(ip + 1 ) :[%c]\n", *(ip + 1 ) ); /* [n]が出る */
//ここまでは想定通り
int i_from_ip;
i_from_ip = *ip;

printf("i_from_ip :[%d]\n", i_from_ip ); /* [1836020329]ではなく[1769107309]が出る */

}

文字列"morin"の前半4byte分は、
m==01101101 o==01101111 r==01110010 i==01101001
となり、結果としては1836020329(10進数)が返ってくると思っていましたが、、

実際には、1769107309(10進数)が返ってきました。
これを2進数に置き換えると、
i==01101001 r==01110010 o==01101111 m==01101101
となっており、何故か逆さまに読み取られているようです。

てっきり頭から4byte分が取得されると思ったのですが。。

何卒ご教授の程、お願い致します。

A 回答 (4件)

「エンディアン」の問題です。


http://ja.wikipedia.org/wiki%E3%82%A8%E3%83%B3%E …

たとえば、32bitなintの場合、8bitずつ区切った4バイトに分けて、それをメモリには記憶されているわけですが、
このように8bitを超える複数バイトなデータを、その時、下位から順番に記録するか、上位から順番に記録するか、どちらから記録するかというのが「エンディアン」です。

以下、2進数で書いてもいいのですが、わかりやすさのため16進数で書きます。
32bitので0x12345678 という数値があった場合、これは8ビットずつ区切ると、0x12, 0x34, 0x56, 0x78 という4バイトになります。

これをメモリの下位から0x12, 0x34, 0x56, 0x78 という順番にメモリに記録するのが「ビッグエンディアン」という方式で、

これをメモリの下位から0x78, 0x56, 0x34, 0x12 という順番にメモリに記録するのが「リトルエンディアン」(言い換えれば、上位から0x12, 0x34, 0x56, 0x78 という順番)です。

どちらのメモリアクセス方式かはCPUによって変わってきます。これはどちらが優れているとか劣っているということはなく、単なる「方式の違い」でしかありません。
世間でよく使われている Intel のx86系CPUなどはリトルエンディアン。

たとえば、
> char arr_c[6] = {'m','o','r','i','n','\0'};
これは、16進数で書くと
> char arr_c[6] = {0x6d, 0x6f, 0x72, 0x69, 0x6e, 0x00};
に相当しますが、

このならびのメモリを、ビッグエンディアンのシステムで32bit数値として読みとった場合は、32bit値は上位から8bitずつ見ると 0x6d, 0x6f, 0x72, 0x69 になる、すなわち 0x6d6f7269=1836020329 になりますが、

このならびのメモリを、リトルエンディアンのシステムで32bit数値として読みとった場合は、32bit値は上位から8bitずつ見ると 0x69, 0x72, 0x6f, 0x6d になる、すなわち 0x69726f6d=1769107309 になる、ということになります。
    • good
    • 0
この回答へのお礼

ご丁寧にありがとうございます。
とても勉強になりました。
データ型に関係なく、リニアに処理されるもの、と誤解していたことが判りました。

ありがとうございました。

お礼日時:2011/01/13 13:08

ちょっと待った.


printf("*ip :[%c]\n", *ip ); /* [m]が出る */
printf("*(ip + 1 ) :[%c]\n", *(ip + 1 ) ); /* [n]が出る */
が, なんで「想定通り」なの? これが本当に「想定通り」なら, その後ろの
「結果としては1836020329(10進数)が返ってくると思っていましたが」
と明らかに矛盾する. これを期待するなら *ip で表示されるのは「i」でないとおかしいよ.
    • good
    • 0
この回答へのお礼

ipのint型(4byte)隣の文字を出せ!
という命令文と解釈していますので、
[m]の4個となりは、[n]と想定した次第です。

とは言え、int型のbyte数は処理系により異なるので、
その注記を漏らしてしまったのはすみませんでした。

ご回答ありがとうございました。

お礼日時:2011/01/13 13:14

回答自体は、すでに書かれているようなことになります。



一見すると、メモリ上に、"mori" ("\x6d\x6f\x72\x69") と並んでいるデータを、数値として扱うと、0x69726f8d になるというのは、不自然に見えますが、これはこれで合理的ではあるのです。
(あくまでも、「これはこれで合理的」なのであって、「この方法だけが合理的」ではありませんが)

たとえばあるアドレスから、"\x01\x00\x00\x00" という順番でデータが存在したとします。
これは、(上記の方法で)4bytes の数値として扱うと 0x00000001 になります。(数値としての1)
同じアドレスから、2bytesの数値(たとえば、short)だとすると、0x0001 でやはり、数値としての1です。
同じアドレスから、1byteの数値(たとえば、char )だとすると、0x01 で、やはり、数値としての1です。

こういう風に(オーバーフローがない限り)どの長さで切り取っても同じ数値になるという性質があるわけです。

これが、逆のビッグエンディアンであれば、このメモリの並びを

4bytes の整数としてみれば、0x01000000 = 16777216
2bytes の整数としてみれば、0x0100 = 256
1byte の整数としてみれば、 0x01 = 1
と、データ長により数値がわかってきてしまいます。

こういう点で、リトルエンディアンでも、それなりに合理的ではあるわけです。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

確かに、int変数をdouble変数に入れるのであれば、
リトルエンディアンの考え方のほうがしっくり来ますね。
大変勉強になりました。

ありがとうございました。

お礼日時:2011/01/13 13:11

一言でいうと「バイトオーダー」によるものです。

    • good
    • 0
この回答へのお礼

早速のご回答ありがとうございます!
調べてみて、ソケット通信で今まで考慮していなかったのを知り、青ざめました。
確かに、言われてみればそりゃそうだという内容でした。

ありがとうございました。

お礼日時:2011/01/13 13:05

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!