「ぽいんた3」(2005/12/22 (木) 23:20:38) の最新版変更点
追加された行は緑色になります。
削除された行は赤色になります。
メモリへのアクセスの制限事項の一つに、「アラインメント」もしくは「WORD境界」というものがある。
これは何かというと、奇数番地のアドレスに対して2バイト以上の型(8bit環境以外でのintなど)でアクセスすることができないという制限だ。
もちろん、すべてのCPUがその制限を持っているわけではない。幸いなことに?われらがPentiumは奇数番地アクセスが可能(たしか)。
具体的に言うと、
unsigned char* ucpHoge[100]; /*100バイトの領域確保*/
unsigned short int usiPiyo; /*short型の変数に*/
usiPiyo = *(unsigned short int*)(ucpHoge + 1);
/* 奇数番地のアドレスを */
/* short*にキャストして */
/* その値をとろうとしている */
/* ちょっとややこしい? */
なんていうアクセスしちゃうとCPU例外が発生したりする。
この問題のいやらしい点は、ロジック上(ソースコード上)ではなかなか発見できないことである。
上の例などではまあすぐにわかるんだけど、問題は可変長のデータを扱っているとき。
実にいやらしいことに、データが可変長なので上のような処理が入っていたとしても上手くいくときと上手くいかない時がある。
なので、単体テストどころか結合テストまですり抜けて、割と後になってデバッガやらLOGやらで散々調べまくってやっと不具合の原因が判明したりする。
じゃあどうすんだよ、というと、データが可変長な場合はWork用の領域なり目的の変数なりにmemcpy。上の例だと最期の式を
memcpy(&usiPiyo, ucpHoge + 1,
sizeof(unsigned short int));
としてやればusiPiyoに目的の値が入るはず。
また、構造体を定義する際にもアラインメントを考慮する必要がある。
以下のような構造体定義があったとする。
typedef struct {
char cAAA;
int iBBB;
char cCCC;
} ST_TEST1;
typedef struct {
char cAAA;
char cCCC;
int iBBB;
} ST_TEST2;
それぞれのサイズはおそらく、(intが4バイトとして)ST_TEST1が7バイト、ST_TEST2が6バイトとなる。
コンパイラが最適化して、アクセスしやすいように(制限のあるCPUではアクセスできるように)それぞれの要素を偶数番地からに置き換えてくれるためである。つまりST_TEST1のcAAAとcBBBの間にはムダな1バイトが入ってしまう。
VC++なんかだと32bitアクセスしやすいように4バイトごとの境界に丸めようとするので、さらに構造体のサイズは変わってくるかもしれない。
(昔のMS-Cなんかだと、#pragma pack(1)だったかな?でそろえないように指定できたけど今はどうなんでしょう??)
上記の二つのアラインメントのお話は、当然ながらコンパイラおよびCPUに依存する。したがって、ある程度それらを意識して回避または吸収できるコードを書かないとプログラムの移植性はどんどん低くなってしまう。
やっぱり、C言語ってポータブルじゃないよなぁ。。
メモリへのアクセスの制限事項の一つに、「アラインメント」もしくは「WORD境界」というものがある。
これは何かというと、奇数番地のアドレスに対して2バイト以上の型(8bit環境以外でのintなど)でアクセスすることができないという制限だ。
もちろん、すべてのCPUがその制限を持っているわけではない。幸いなことに?われらがPentiumは奇数番地アクセスが可能(たしか)。
具体的に言うと、
unsigned char* ucpHoge[100]; /*100バイトの領域確保*/
unsigned short int usiPiyo; /*short型の変数に*/
usiPiyo = *(unsigned short int*)(ucpHoge + 1);
/* 奇数番地のアドレスを */
/* short*にキャストして */
/* その値をとろうとしている */
/* ちょっとややこしい? */
なんていうアクセスしちゃうとCPU例外が発生したりする。
この問題のいやらしい点は、ロジック上(ソースコード上)ではなかなか発見できないことである。
上の例などではまあすぐにわかるんだけど、問題は可変長のデータを扱っているとき。
実にいやらしいことに、データが可変長なので上のような処理が入っていたとしても上手くいくときと上手くいかない時がある。
なので、単体テストどころか結合テストまですり抜けて、割と後になってデバッガやらLOGやらで散々調べまくってやっと不具合の原因が判明したりする。
じゃあどうすんだよ、というと、データが可変長な場合はWork用の領域なり目的の変数なりにmemcpy。上の例だと最期の式を
memcpy(&usiPiyo, ucpHoge + 1,
sizeof(unsigned short int));
としてやればusiPiyoに目的の値が入るはず。
また、構造体を定義する際にもアラインメントを考慮する必要がある。
以下のような構造体定義があったとする。
typedef struct {
char cAAA;
int iBBB;
char cCCC;
} ST_TEST1;
typedef struct {
char cAAA;
char cCCC;
int iBBB;
} ST_TEST2;
それぞれのサイズはおそらく、(intが4バイトとして)ST_TEST1が7バイト、ST_TEST2が6バイトとなる。
コンパイラが最適化して、アクセスしやすいように(制限のあるCPUではアクセスできるように)それぞれの要素を偶数番地からに置き換えてくれるためである。つまりST_TEST1のcAAAとcBBBの間にはムダな1バイトが入ってしまう。
VC++なんかだと32bitアクセスしやすいように4バイトごとの境界に丸めようとするので、さらに構造体のサイズは変わってくるかもしれない。
(昔のMS-Cなんかだと、#pragma pack(1)だったかな?でそろえないように指定できたけど今はどうなんでしょう??)
上記の二つのアラインメントのお話は、当然ながらコンパイラおよびCPUに依存する。したがって、ある程度それらを意識して回避または吸収できるコードを書かないとプログラムの移植性はどんどん低くなってしまう。
やっぱり、C言語ってポータブルじゃないよなぁ。。
表示オプション
横に並べて表示:
変化行の前後のみ表示: