ページ 11

アライメント[alignment]に関する難しい質問

Posted: 2010年8月15日(日) 10:11
by プログラマー
はじめまして

alignmentは具体的な言語に依存しないCPUの問題だと認識していますが、
便宜上ここではC言語の例にして問題の提起をさせていただきたいです。

例えば、よく見かける身ビットマップのC言語での実装例ですけれども、
ほとんどの場合、ファイルヘッダやinfoヘッダの構造体を
(BYTEストリームとして)丸ごとファイルに書き込もうとする実装ですね。

ところが、アライメント関係で構造体のメンバーの間や構造体の後部分必ず隙間(padding)があって、
ごみデータがファイルに書き込まれてしまうばかりでなくて、
sizeofによる取得した構造体のサイズも実際の有効データより大きくて、
ファイルの決まったフォーマットを破れるのではないかという問題ですね。

解説できる方お願いします。

Re:アライメント[alignment]に関する難しい質問

Posted: 2010年8月15日(日) 10:33
by Poco
うーん、定義済み(変更不可)のファイルフォーマットがあるのに、
構造体を丸ごと書き込もうとかいう実装はありえないと思うのですが。

何が問題なのかよく分からないです。
同一プラットフォームで読み書きするなら、パディングのゴミデータが
悪さするわけでもないし、
異種プラットフォームで読み書きするなら、少なくとも
ファイルフォーマットが厳密に定義されているはずで、
先程も言ったように、丸ごと書き込む実装ってのはあり得ないですし。

Re:アライメント[alignment]に関する難しい質問

Posted: 2010年8月15日(日) 10:47
by softya
アラメインとは構造体の書き方でいくらでも調整可能です。
有名なコンパイラなどは、コンパイルオプションや#pragmaなどでパディングを変更可能ですね。

Re:アライメント[alignment]に関する難しい質問

Posted: 2010年8月15日(日) 19:52
by プログラマー
皆さんご返答有難うございます

例えば、以下は典型的なビットマップを作成するC言語コードですけれども。
ヘッダを構造体としてsizeofでメモリを確保し、ヘッダ中の既定情報を構造体のメンバーとして設定して、
画像データと貫いて、ファイルに一発で書き込むというやり方ですね。

構造体の中にpaddingごみがいっぱいあると同時に、sizeofで確保した構造体のサイズは実際の有用データの合計値より大きいはずですーーーファイルのフォーマットはどうして保持できるのでしょか。


// メモリイメージ用ポインタ設定
lpHead=(LPBITMAPFILEHEADER)lpBufl;
lpInfol=(LPBITMAPINFOHEADER)((LPBYTE)lpBufl+sizeof(BITMAPFILEHEADER));
lpPixell=(LPBYTE)lpBufl+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);

// ビットマップのヘッダ作成
lpHead->bfType='M'*256+'B';
lpHead->bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwLength*dwHeight;
lpHead->bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
lpInfol->biSize=sizeof(BITMAPINFOHEADER);
lpInfol->biWidth=dwWidth;
lpInfol->biHeight=dwHeight;
lpInfol->biPlanes=1;
lpInfol->biBitCount=24;

// ピクセル列をコピー
CopyMemory(lpPixell,lpPixel,dwLength*dwHeight);

fh=CreateFile(lpszFn,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

WriteFile(fh,lpBufl,sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFO)+dwLength*dwHeight,&dwSize,NULL);// ヘッダと画像データを全部一気に書き込む;biBitCount==24 の場合、カラーテーブルは領域ない!

CloseHandle(fh);

Re:アライメント[alignment]に関する難しい質問

Posted: 2010年8月15日(日) 20:40
by softya
BITMAPFILEHEADERだと
#pragma pack(push, 2)
が無いとマズイでしょうね。

WindowSDKだとちゃんとパディング問題が起きないようになっているはずですよ。
サイズは確認はされたのですか?
もし、14バイトならパティングはちゃんと調整されていると思いますが。

Re:アライメント[alignment]に関する難しい質問

Posted: 2010年8月15日(日) 21:30
by Poco
> 画像データと貫いて、ファイルに一発で書き込むというやり方ですね。

すみません、やっぱり問題が理解できません。
ファイルに一発で書き込めないことの何が問題なのでしょうか?

Re:アライメント[alignment]に関する難しい質問

Posted: 2010年8月15日(日) 22:27
by dic
何を言ってるのか私にもさっぱり・・・

Re:アライメント[alignment]に関する難しい質問

Posted: 2010年8月16日(月) 00:36
by ISLe
> 構造体の中にpaddingごみがいっぱいあると同時に、sizeofで確保した構造体のサイズは実際の有用データの合計値より大きいはずですーーーファイルのフォーマットはどうして保持できるのでしょか。

BITMAPFILEHEADER等が定義されているWinGDI.hを見れば分かりますが、隙間ができないようにアラインメントが調整されています。

なのでCPUによってはメンバにアクセスしようとするだけで落ちるでしょう。
その場合該当メンバはバイト単位で読み書きするなどして対応する必要があります。

いまのところWindowsプラットフォームでは落ちるような問題にはなっていないだけですね。
でも例えば32ビットピクセルのビットマップでファイルイメージそのまま読み込んだらピクセルデータ部分のアラインメントがずれて、ピクセルデータに対するDWORDアクセスの速度が低下するようなことはあるでしょう。

Re:アライメント[alignment]に関する難しい質問

Posted: 2010年8月16日(月) 15:15
by プログラマー
皆さん
丁寧なご指導有難うございました。
MSの*.hファイルにできるだけ依存しないようなプログラムを組みたいので、
単独にBTMファイルのヘッダを自分のプログラムに定義したのですけれども(無論BTMファイルの規格に準じて)。
それでもだめで、MSのほうはpragmaも”ひそかに”使っているのですね。
MSにトリックされたような気持ちです(^^)

pragmaって規格もなく、その内容や動きは各社勝手に定義しているでしょ。

ファイルに書き込む内容に関して、
基本は構造体の形で表すのは可笑しいで、禁じるべきだと個人的に考えていますが、どうでしょう。

例えば、ファイルヘッダのフィールド名で先頭からのoffsetを#defineで定義して、
直接そのバッファメモリのアドレスにアクセスすればいいでは、と思います(キャストを利用とか)

Re:アライメント[alignment]に関する難しい質問

Posted: 2010年8月16日(月) 15:27
by softya
それでもエンディアン問題があるので、パディングだけが解決しても完全な汎用ではありませんよ。あと、例えdefineとかで定義されてもx86系はアドレス境界(アライメント)例外を起こさないので良いですが、もし68000系のようなアドレス境界(アライメント)例外を起こすCPUの場合は1バイトづつ書き込むしかありません。
他にも色々なCPUがありますので、汎用的な定義は難しいんですよ。

【余談】
何度かC言語でエンディアン記述子が欲しいと思ったことがあります。 画像

Re:アライメント[alignment]に関する難しい質問

Posted: 2010年8月16日(月) 16:08
by toyo
私も最初にビットマップ扱ったときに構造体のパックを知らずにメンバー単位で読み書きしてたことがありました
wingdi.hでビットマップ用構造体が定義されているのも知りませんでしたから自分で構造体定義を書いてましたし

Re:アライメント[alignment]に関する難しい質問

Posted: 2010年8月17日(火) 02:13
by プログラマー
また貴重なご意見いただき、ありがとうございます

>何度かC言語でエンディアン記述子が欲しいと思ったことがあります。

なるほど! エンディアンも実にややこしいですね