パスワードを忘れた? アカウント作成
837303 journal
プログラミング

EarOwlの日記: エンディアン・アライメント・int型のサイズ 7

日記 by EarOwl

C プログラマ、特に組込系のプログラマにとって、移植性の高いコードを書く上で重要でありながら意外とハマる人が多いのが、タイトルに挙げた 3点だと思う。

エンディアンの変換で重要なのは、以下の 2点。

  1. ビッグ・リトルといった固定のエンディアンと『CPU のネイティブなエンディアン』を分けて考える
  2. どのエンディアンからどのエンディアンへ変換したいのかが明確になるような関数名/マクロ関数名にする

swap16/32 という関数/マクロ関数名をよく見かけるが、これだとどのエンディアンからどのエンディアンへ変更したいのかが分からないため、移植時に問題となる可能性がある。

アライメントに関しては、構造体のパディングは理解していても、例えば以下のようなコードで問題が発生する場合があることを理解していない人が多いように思う。

void fill_buffer(uint8_t *buf, uint32_t n)
{
    *(uint32_t *)buf = n;
}

C 言語の仕様として、 buf が uint32_t 型の境界にアラインされていない場合の動作は未定義となる。例えば、 CPU によってはアドレスの下位 2ビットが無視されてしまい、誤ったアドレスに値を書き込んでしまう結果となることがある。

int 型のサイズに関しては、理解していてもなかなか難しい。私も以下のようなコードを書いてハマったことがある。

uint32_t n;
uint8_t buf[4];
/* … */
n = buf[0] * 0x1 + buf[1] * 0x100 + buf[2] * 0x10000 + buf[3] * 0x1000000;

演算子の優先順位、型変換の規則をきっちり理解して、コードを書く際には常に注意を払うことが必要だと思う。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
  • by miyuri (33181) on 2011年10月22日 21時40分 (#2038600) 日記

    どのエンディアンからどのエンディアンへ変換したいのかが明確に

    入力時の変換は入力側、出力時は出力側だけ表す方が楽だと書いてみるテスト。

    • by EarOwl (24188) on 2011年10月22日 22時10分 (#2038611) 日記

      確かに入力なら『どのエンディアンへ』の方が、出力なら『どのエンディアンから』の方が CPU のネイティブエンディアンになるのが普通だから、それで十分ですね。

      私は基本的にエンディアンやアライメントの異なるデータは uint8_t の配列で受け渡しを行い、 uint16/32_t 型の変数は必ず CPU のネイティブなエンディアンとなるようにしています。

  • by Anonymous Coward on 2011年10月22日 15時47分 (#2038485)

    uint32_t n;
    uint8_t buf[4];
    /* … */
    n = buf[0] * 0x1 + buf[1] * 0x100 + buf[2] * 0x10000 + buf[3] * 0x1000000;

    int=int32_tの処理系だとして、定数の部分はUL(unsigned long=uint32_tとして)をつけておかないと乗算の結果がオーバーフローしてそれが問題になるのかもしれないですが、どうでしょう?
    正解もこっそり教えて下さい。

    • by fl (43569) on 2011年10月22日 16時21分 (#2038495) 日記

      普通はシフト演算でやらね?

      n = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;

      • by EarOwl (24188) on 2011年10月22日 17時36分 (#2038516) 日記

        int が 16bit のとき、 buf[1] * 0x100 が負の値となってしまう場合があります。
        これを 32bit に型変換する際に上位 16bit が 0x0000 になって欲しいところが
        0xFFFF になってしまい問題となります。

        ビットシフトで行う場合も、 fl さんのコメント中のコードでは int が 16bit の場合に
        16ビット及び 24ビットのシフトがビット幅以上のシフトになってしまい、結果は
        未定義となります。

typodupeerror

目つきのヤバい少年がナイフをシュッ・シュッと振り回しながら街を徘徊している情景が目に浮かんだ -- あるセキュリティ専門家

読み込み中...