C言語関係掲示板

過去ログ

No.1168 バイナリデータを数値に変換

[戻る] [ホームページ]
No.15217

バイナリデータを数値に変換
投稿者---ねこのひげ(2004/07/05 04:48:50)


こんにちは。
今、VisualStudio6.0で、下記のようなコードを書いてみました。
--------------------
void main()
{
    typedef struct
    {
        char hoge[1];
        char LL[4];
        char moge[2];
    } STR;
    char charX[]={0x61, 0x30, 0x31, 0x32, 0x33, 0x62, 0x63, 0x00};
    STR *str;
    long Len;

    str = (STR *)charX;
    memcpy(&Len, &(str->LL), sizeof(long));
}
--------------------
そうすると、
Lenの値は、858927408になりますが、なぜ、808530483にならないのでしょうか?(0x3031323334を10進に変換した値ではなく、0x3433323130を変換した値になってしまう。)

また、構造体を
-----------------
typedef struct
{
    char hoge[1];
    long LL;
    char moge[2];
} STR;
-------------
として同様のことをすると、Lenの値は、6513203(0x636233を10進にしたもの)になってしまいます。
なぜlongにすると、&(str->LL)が変わってしまうのでしょうか?

どうぞよろしくお願いいたします。



No.15218

Re:バイナリデータを数値に変換
投稿者---ぽへぇ(2004/07/05 05:57:18)


>Lenの値は、858927408になりますが、なぜ、808530483にならないのでしょうか?
>(0x3031323334を10進に変換した値ではなく、0x3433323130を変換した値になってしまう。)

0x33323130ですね。
Pentium系(インテル系)のCPUは歴史的な経緯により、最下位のバイトから
順番に置かれる形になります(見た目は逆順になります)。
「リトル・エンディアン」で検索してみてください。

>また、構造体を
>typedef struct
>{
>    char hoge[1];
>    long LL;
>    char moge[2];
>} STR;
    :
>&(str->LL)が変わってしまうのでしょうか?

コンパイラは構造体を扱いやすいように詰め物(パディング)をします。
以下のように pragma pack で囲むとパディングをしないようになり、
意図した位置にLLが来ると思います。
(上記エンディアンの関係上、Lenは0x33323130になりますが)

#pragma pack (1)
    typedef struct
    {
        char hoge[1];
        long LL;
        char moge[2];
    } STR;
#pragma pack()

(このpragmaはコンパイラに依存するので、VisualC以外では
  使えないかもしれません)




No.15219

Re:バイナリデータを数値に変換
投稿者---とおり(2004/07/05 06:37:40)


>「リトル・エンディアン」で検索してみてください。

こちらは、構造体内の各バイトの値を確認して見ると、よく分かるでしょう。
# バイト単位で確認することが大事です。
# それ以上の単位だと、メモリから読み込む時点で、既にエンディアンの調整
# がされてしまうので、実際のメモリ上の値が分からないです。

>コンパイラは構造体を扱いやすいように詰め物(パディング)をします。
>以下のように pragma pack で囲むとパディングをしないようになり、

こちらは、構造体の各メンバの実際のポインタ値をprintfの%pで確認してみると
良いでしょう。そもそも、longを4バイトと決めつけているのも危険です。
また、なるべくpragmaによるコンパイラ依存な書き方はしないことをお勧めします。


No.15220

Re:バイナリデータを数値に変換
投稿者---とおり(2004/07/05 06:44:01)


http://www.kouno.jp/home/c_faq/c2.html#14

こちらのマクロ、および、周辺項目も参考にどうぞ。


No.15238

Re:バイナリデータを数値に変換
投稿者---ねこのひげ(2004/07/05 16:52:54)


こんにちは。
ぽへぇ様、とおり様、ありがとうございます。

> 0x33323130ですね。

そうです。失礼致しました。
正しくは、

Lenの値は、858927408になりますが、なぜ、808530483にならないのでしょうか?
(0x30313233を10進に変換した値ではなく、0x33323130を変換した値になってしまう。)

です。

> Pentium系(インテル系)のCPUは歴史的な経緯により、最下位のバイトから
> 順番に置かれる形になります(見た目は逆順になります)。
> 「リトル・エンディアン」で検索してみてください。

調べました。納得いたしました。ありがとうございます。
今まで、エンディアンは、ダブルバイト文字にしか関係しないのかと思っておりました。

> コンパイラは構造体を扱いやすいように詰め物(パディング)をします。

こちらは、今まで、全く知りませんでした。

構造体を
-----------------
typedef struct
{
    char hoge[1];
    long LL;
    char moge[2];
} STR;
-------------
で宣言した時、
各メンバの先頭アドレスと値、サイズ、教えてくださったHPにあったオフセットを取得するマクロで値をとってみました。
結果は、

・hoge  先頭アドレス=0x0012ff78  値='a'       サイズ=1  オフセット=0
・LL    先頭アドレス=0x0012ff7c  値=6513203   サイズ=4  オフセット=4
・moge  先頭アドレス=0x0012ff80  値=?(不明) サイズ=2  オフセット=8

(sizeof(STR)=12)

となって、間にパディングがある事は分かりました。
ただ、そうしますと、
#pragma packを使わずに、正確なLL、mogeを取得したい場合は、
構造体を全てchar型にするしかないのでしょうか?



No.15240

Re:バイナリデータを数値に変換
投稿者---REE(2004/07/05 17:01:40)


>#pragma packを使わずに、正確なLL、mogeを取得したい場合は、
>構造体を全てchar型にするしかないのでしょうか?

通常は構造体のメンバのオフセットに依存しないようなプログラムを作成します。
あなたのいう正確なLL、mogeとは何でしょうか?


No.15243

Re:バイナリデータを数値に変換
投稿者---ねこのひげ(2004/07/05 18:34:35)


REE様、ありがとうございます。

>あなたのいう正確なLL、mogeとは何でしょうか?

ここでの例では、
hogeは文字列の0番目〜1バイト分の値、
LLは文字列の1番目〜4バイト分の値、
mogeは文字列の5番目〜2バイト分の値
です。

>通常は構造体のメンバのオフセットに依存しないようなプログラムを作成します。

現在、最大15万バイトの、バイナリ・ANK・漢字が混ざっている文字列を、
その文字列の値を見ながら、何バイト目〜何バイト目までは文字コードをJISに変換、何バイト目〜何バイト目まではSJISに変換・・・
というようなことをしています。
もともとあったソースに機能を追加していくのですが、その、もともとのソースで
分からない部分を抜き出して、VisualStudioでテストしていた所、今回の疑問にぶち当たりました。
既存の部分を手直しすることはできないのですが、普通、こういった場合、
どのようにするのが最適な方法なのでしょうか。



No.15246

Re:バイナリデータを数値に変換
投稿者---REE(2004/07/05 19:31:28)


>>あなたのいう正確なLL、mogeとは何でしょうか?

>ここでの例では、
>hogeは文字列の0番目〜1バイト分の値、
>LLは文字列の1番目〜4バイト分の値、
>mogeは文字列の5番目〜2バイト分の値
>です。

パディングのない構造体を使いたいということですね。
しかし、一般的にパディングの有無はコンパイラに依存するものです。
charの場合には、パディングが無いという保証があるかは知りませんが・・

>通常は構造体のメンバのオフセットに依存しないようなプログラムを作成します。

>現在、最大15万バイトの、バイナリ・ANK・漢字が混ざっている文字列を、
>その文字列の値を見ながら、何バイト目〜何バイト目までは文字コードを>JISに変換、何バイト目〜何バイト目まではSJISに変換・・・
>というようなことをしています。

文字列の値を見ながらということは、何バイト目かは可変なのでしょうか?

どちらにしても、自分なら、文字列は全体を一つのバッファに入れて、
別の構造体で文字セットの種類、開始位置、終了位置を管理すると思います。


No.15262

Re:バイナリデータを数値に変換
投稿者---ねこのひげ(2004/07/06 08:49:51)


REE様、ありがとうございます。

>文字列の値を見ながらということは、何バイト目かは可変なのでしょうか?

はい。そうです。
もう少し詳しく説明しますと、例えば
文字列の、3バイト目と4バイト目の値が11なら、
次の10バイトはJISに、その次の2バイトはSJISに変換、
3バイト目と4バイト目の値が12なら、
変換する文字数も変わります。
そして、更にその続きのxバイト目の値が21なら・・・
と続いていくのです。

>どちらにしても、自分なら、文字列は全体を一つのバッファに入れて、
>別の構造体で文字セットの種類、開始位置、終了位置を管理すると思います。

なるほど。
すると、今回のような、バッファへのポインタを構造体にキャストしたり、
バッファを構造体にコピーしたり、というのは、パディングがある以上、
使わない方が良いのですね。

勉強になりました。ありがとうございました。



No.15264

Re:バイナリデータを数値に変換
投稿者---REE(2004/07/06 10:26:15)


>>文字列の値を見ながらということは、何バイト目かは可変なのでしょうか?

>はい。そうです。
>もう少し詳しく説明しますと、例えば
>文字列の、3バイト目と4バイト目の値が11なら、
>次の10バイトはJISに、その次の2バイトはSJISに変換、
>3バイト目と4バイト目の値が12なら、
>変換する文字数も変わります。
>そして、更にその続きのxバイト目の値が21なら・・・
>と続いていくのです。

ということは、その値によって、構造体を変える予定だったのでしょうか?
かなりややこしいことになりそうです・・

もし、文字数などが、不規則になっているのなら、そのルールを構造体のテーブル(配列)で持つようにするとすっきりしそうです。

>すると、今回のような、バッファへのポインタを構造体にキャストしたり、
>バッファを構造体にコピーしたり、というのは、パディングがある以上、
>使わない方が良いのですね。

場合によりますね、それを使った方がいい場合もあります。(特定のファイルのヘッダ解析など)
その場合は構造体のパディングやエンディアンの依存を強く受けますので、
注意が必要です。


No.15291

Re:バイナリデータを数値に変換
投稿者---ねこのひげ(2004/07/06 19:11:13)


REE様、ありがとうございます。

>ということは、その値によって、構造体を変える予定だったのでしょうか?
>かなりややこしいことになりそうです・・

はい。値によって、使用する構造体を変えるつもりでした。

>もし、文字数などが、不規則になっているのなら、そのルールを構造体のテーブル(配列)で持つようにするとすっきりしそうです。

なるほど〜です。
今回は、ソースの大幅な変更はできないので、この方法は使えないのですが、
今度自分で何かを作る時、是非、参考にさせていただきます。
(なんだか頭がこんがらがりそうですが・・・)

>その場合は構造体のパディングやエンディアンの依存を強く受けますので、
>注意が必要です。

そうですね。
使ったとしても、この場合、移植性が低くなるのはしょうがないのですよね・・。(同じUNIXでも、32bitマシンと64bitマシンではパディングが変わってしまいますよね。)