質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.46%

  • C

    4685questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

構造体のキャストと参照

解決済

回答 2

投稿

  • 評価
  • クリップ 2
  • VIEW 2,149

izuki_y

score 47

ビットマップのファイルを読み出してヘッダ情報を取得する関数を自作しました。
しかし取得したヘッダ情報が正しく表示されません(構造体のアライメント問題)

処理を簡単に説明すると、fReadしたビットマップデータのポインタを
ビットマップヘッダ構造体のポインタにキャストして参照しています。

メンバ変数ごとにオフセットと変数のサイズを定義して一つづつキャストしていけば、
問題ない事は確認できましたがメンバ一つ一つに対してこの処理を行うのは冗長だと思います。
少ないコードで分かりやすく綺麗に参照する方法があればぜひ教えてください。

#include <stdio.h>
#include <stdlib.h>

/* unsigned */
typedef unsigned long       UINT32;   /* 4byte */
typedef unsigned short      UINT16;   /* 2byte */
typedef unsigned char       UINT8;    /* 1byte */
typedef unsigned char       BYTE;     /* 1byte */

typedef struct __tagBITMAPFILEHEADER {
    UINT16  bfType;         /* ファイルタイプ 'BM' - OS/2, Windows Bitmap */
    UINT32  bfSize;         /* ファイルサイズ (byte) */
    UINT16  bfReserved1;    /* 予約領域 常に 0 */
    UINT16  bfReserved2;    /* 予約領域 常に 0 */
    UINT32  bfOffBits;      /* ファイル先頭から画像データまでのオフセット (byte) */
}BITMAPFILEHEADER;

/* Windows */
typedef struct __tagBITMAPINFOHEADER{
    UINT32      biSize;         /* 情報ヘッダサイズ[40byte]         */
    UINT32      biWidth;        /* 画像の幅[ピクセル]               */
    UINT32      biHeight;       /* 画像の高さ[ピクセル]             */
    UINT16      biPlanes;       /* プレーン数 常に1                 */
    UINT16      biBitCount;     /* 色ビット数[bit] 1,4,8,(16),24,32 */
    UINT32      biCompression;  /* 圧縮形式 0,1,2,3                 */
    UINT32      biSizeImage;    /* 画像データサイズ[byte]           */
    UINT32      biXPixPerMeter; /* 水平解像度[dot/m]                */
    UINT32      biYPixPerMeter; /* 垂直解像度[dot/m]                */
    UINT32      biClrUsed;      /* 格納パレット数[使用色数]         */
    UINT32      biCirImportant; /* 重要なパレットのインデックス     */
}BITMAPINFOHEADER;

int main(){
    FILE *fp;
    BYTE *pBmpData = NULL;
    size_t rdsize;
    fpos_t fsize = 0;
    BITMAPFILEHEADER bmfh, *pbmfh;
    BITMAPINFOHEADER bmih, *pbmih;

    if (fopen_s(&fp, "hogehoge.bmp", "rb") != 0){
        printf("Read File Open Error. \n");
        return -1;
    }

    /* get file size */
    fseek(fp, 0, SEEK_END);
    fgetpos(fp, &fsize);
    fseek(fp, 0, SEEK_SET);

    /* allocate memory */
    pBmpData = (BYTE*)malloc(sizeof(BYTE) * (size_t)fsize);
    if (pBmpData == NULL){
        return -1;
    }
    /* read Bitmap file */
    rdsize = fread(pBmpData, sizeof(BYTE), (size_t)fsize, fp);
    if (rdsize != fsize) {
        return -1;
    }


#if 0
    // これも駄目
    memcpy(pbmfh, pBmpData, sizeof(BITMAPFILEHEADER));
    memcpy(pbmfh, pBmpData + sizeof(BITMAPFILEHEADER), sizeof(BITMAPINFOHEADER));
#else
    // これも駄目
    pbmfh = (BITMAPFILEHEADER*)(pBmpData);
    pbmih = (BITMAPINFOHEADER*)(pBmpData + sizeof(BITMAPFILEHEADER));

    // これも駄目
    bmfh = *((BITMAPFILEHEADER*)(pBmpData));
    bmih = *((BITMAPINFOHEADER*)(pBmpData + sizeof(BITMAPFILEHEADER)));
#endif
    /* BITMAPFILEHEADER */
    printf("bfType     = 0x%x\n", pbmfh->bfType);
    printf("bfSize     = 0x%x\n", pbmfh->bfSize);
    printf("bfReserved1= 0x%x\n", pbmfh->bfReserved1);
    printf("bfReserved2= 0x%x\n", pbmfh->bfReserved2);
    printf("bfOffBits  = 0x%x\n", pbmfh->bfOffBits);

    /* BITMAPINFOHEADER */
    printf("biSize        = 0x%x\n", pbmih->biSize);
    printf("biWidth       = 0x%x\n", pbmih->biWidth);
    printf("biHeight      = 0x%x\n", pbmih->biHeight);
    printf("biPlanes      = 0x%x\n", pbmih->biPlanes);
    printf("biBitCount    = 0x%x\n", pbmih->biBitCount);
    printf("biCompression = 0x%x\n", pbmih->biCompression);
    printf("biSizeImage   = 0x%x\n", pbmih->biSizeImage);
    printf("biXPixPerMeter= 0x%x\n", pbmih->biXPixPerMeter);
    printf("biYPixPerMeter= 0x%x\n", pbmih->biYPixPerMeter);
    printf("biClrUsed     = 0x%x\n", pbmih->biClrUsed);
    printf("biCirImportant= 0x%x\n", pbmih->biCirImportant);

    return 0;
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

コンパイラが分からないので適切かどうか分かりませんが
VC++やC++ Builderなら#pragma pack(サイズ) でアラインメントを変更できます。
以下を参照
http://docs.embarcadero.com/products/rad_studio/radstudio2007/RS2007_helpupdates/HUpdate4/JA/html/devwin32/pndsnpragmapack_xml.html

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/04/10 21:29

    cateye さんありがとうございました。
    確認を致しました所、期待する動作になりました。
    こんな方法があったとは目から鱗です。

    VC++だけでなくgccでも使えるみたいですね。

    ```
    #pragma pack(1) // 追加
    typedef struct __tagBITMAPFILEHEADER {
    UINT16 bfType; /* ファイルタイプ 'BM' - OS/2, Windows Bitmap */
    UINT32 bfSize; /* ファイルサイズ (byte) */
    UINT16 bfReserved1; /* 予約領域 常に 0 */
    UINT16 bfReserved2; /* 予約領域 常に 0 */
    UINT32 bfOffBits; /* ファイル先頭から画像データまでのオフセット (byte) */
    }BITMAPFILEHEADER;


    // 修正前
    sizof(BITMAPFILEHEADER) = 16
    sizof(BITMAPINFOHEADER) = 40
    bfType = 0x4d42
    bfSize = 14
    bfReserved1 = 0
    bfReserved2 = 54
    bfOffBits = 2621440

    biSize = 41943040
    biWidth = 31457280
    biHeight = 65536
    biPlanes = 24
    biBitCount = 0
    biCompression = 0
    biSizeImage = 185729024
    biXPixPerMeter= 185729024
    biYPixPerMeter= 0
    biClrUsed = 0
    biCirImportant= 1196228608


    // 修正後
    sizof(BITMAPFILEHEADER) = 14
    sizof(BITMAPINFOHEADER) = 40
    bfType = 0x4d42
    bfSize = 921656
    bfReserved1 = 0
    bfReserved2 = 0
    bfOffBits = 54

    biSize = 40
    biWidth = 640
    biHeight = 480
    biPlanes = 1
    biBitCount = 24
    biCompression = 0
    biSizeImage = 0
    biXPixPerMeter= 2834
    biYPixPerMeter= 2834
    biClrUsed = 0
    biCirImportant= 0

    キャンセル

  • 2015/04/11 22:06

    #pragmaうまくいってよかったです・・・
    gccは私も確認しましたが投稿した後でした^^;

    キャンセル

0

cateye さんに教えて頂いた通り VC や gcc を使う場合は#pragma packが有効なのがわかりました。
しかしそれ以外の環境でやる場合は、やはりポインタで1バイトづつずらして任意の型キャストして使用するしかないようですね。

他に方法が見つからなかった・・・。

    /* BITMAPFILEHEADER */
    printf("bfType     = 0x%x\n", *(UINT16*)(pBmpData    ));    // pbmfh->bfType
    printf("bfSize     = 0x%x\n", *(UINT32*)(pBmpData+2    ));    // pbmfh->bfSize
    printf("bfReserved1= 0x%x\n", *(UINT16*)(pBmpData+6    ));    // pbmfh->bfReserved1
    printf("bfReserved2= 0x%x\n", *(UINT16*)(pBmpData+8    ));    // pbmfh->bfReserved2
    printf("bfOffBits  = 0x%x\n", *(UINT32*)(pBmpData+10));    // pbmfh->bfOffBits

    /* BITMAPINFOHEADER */
    printf("biSize        = 0x%x\n", *(UINT32*)(pBmpData+14    ));    // pbmih->biSize
    printf("biWidth       = 0x%x\n", *(UINT32*)(pBmpData+18    ));    // pbmih->biWidth
    printf("biHeight      = 0x%x\n", *(UINT32*)(pBmpData+22    ));    // pbmih->biHeight
    printf("biPlanes      = 0x%x\n", *(UINT16*)(pBmpData+24    ));    // pbmih->biPlanes
    printf("biBitCount    = 0x%x\n", *(UINT16*)(pBmpData+26    ));    // pbmih->biBitCount
    printf("biCompression = 0x%x\n", *(UINT32*)(pBmpData+28    ));    // pbmih->biCompression
    printf("biSizeImage   = 0x%x\n", *(UINT32*)(pBmpData+32    ));    // pbmih->biSizeImage
    printf("biXPixPerMeter= 0x%x\n", *(UINT32*)(pBmpData+36    ));    // pbmih->biXPixPerMeter
    printf("biYPixPerMeter= 0x%x\n", *(UINT32*)(pBmpData+40    ));    // pbmih->biYPixPerMeter
    printf("biClrUsed     = 0x%x\n", *(UINT32*)(pBmpData+44    ));    // pbmih->biClrUsed

冗長で嫌ですが。
まぁ可読性を上げるならマジックナンバーを使わずに BMFH_TYPE_OFFSET などのマクロ定義をして使うしかないですね。

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.46%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C

    4685questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。