ホーム » スタッフ » 斉藤徹 » ワード境界,共用体,列挙型

2013年11月
« 10月   12月 »
 12
3456789
10111213141516
17181920212223
24252627282930

最近の投稿(電子情報)

アーカイブ

カテゴリー

ワード境界,共用体,列挙型

構造体の演習に引き続き、構造体の応用について説明する。

ワード境界

構造体の配列をメモリに格納する場合、ワード境界が問題となる。 最近のコンピュータでは、CPUの速度に比べ主記憶の速度は遅いため、 メモリを効率よくアクセスする必要がある。 このために、メモリ上のデータは、ワード単位で一括して読み書きされる。

struct Data {
   char      a[ 5 ] ;
   int       b ;
   short int c ;
} ;
struct Data array[ 10 ] ;

上記のような構造体があったとして、メモリー上に以下のように配置されていると、 array[0].b を参照する際には、04行と08行の2回のメモリアクセスがあって、 メモリ参照速度が低下する。

+0 +1 +2 +3
00 a a a a
04 a b b b
08 b c c a
12 a a a a
16 b b b b
20 c c a a

ワードアライメントを無視した配置

+0 +1 +2 +3
00 a a a a
04 a x x x
08 b b b b
12 c c x x
16 a a a a
20 a x x x

ワードアライメントした配置

このような、データを読み書きする際の、ワードの塊の境を「ワード境界」と呼ぶ。 また、右図のように、ワード境界をまたがないようにデータを配置することを、 ワードアライメントと呼ぶ。

もし、処理速度の低下より、メモリの有効利用が必要であれば、VC++であれば、”#pragma pack”や”gccの”__attribute__((packed))”などを指定する。

共用体

プログラムを作っていると、データが「名前と年齢」か「名前と住所」のどちらかで、 「名前と年齢と住所」が揃うことはない…という場合もある。この際に、”struct ..{ char name[] ; int age ; char addr[] } ; ” では、かならず3つのデータを覚えてしまい、メモリの無駄が発生するのが問題となる場合がある。 このようなときに使うのが共用体である。共用体では、中身のいずれかを覚えるために使う。 また、要素の記憶場所は共有される。

union AgeOrAddr {
   int age ;
   char addr[ 30 ] ;
} ;
struct NameAndAgeOrAddr {
   char name[ 20 ] ;
   union AgeOrAddr age_addr ;
} ;

データの記憶場所が同じことを確認するために、以下の様な処理を実行した結果も示す。

union A {
   char s[4] ;
   int  x ;
} ;
union A a ;
// 文字列の代入
strcpy( a.s , "ABC" ) ;
printf( "%8x\n" , a.x ) ;
// 配列の代入
a.x = 0x12345678 ;
a.s[2] = 0x99 ;
printf( "%8x\n" , a.x ) ;
(( 実行結果 ))
00434241     // 実験したコンピュータは Intel Xeon
11995678     // この結果より、リトルエンディアン なのが判る。

この結果は、CPUの内部が、ビッグエンディアン(68系,SPARC)か、リトルエンディアン(x86系)によって異なる。また、この順番はCPUの異なる間のネットワーク通信でも問題となることが多く、ネットワーク通信では、ビッグエンディアンを用いることになっている。

列挙型

カレンダーの処理などで、週のデータを扱う場合、日:0,月:1,火:2… と番号を振って その番号に応じてプログラムを記述する場合がある。

(( わかりにくいプログラム ))
int week = なにかの週番号 ;
if ( week == 3 ) {  // 3がマジックナンバー
   水曜日の処理 ;
}
(( わかりやすいプログラム ))
#define SUN 0
#define MON 1
#define TUE 2
:
if ( week == WED ) { // 読んでいて意味が判る
   水曜日の処理 ;
}
(( でも週と月を整数型で扱っているだけ ))
#define JAN 1
#define FEB 2
#define MAR 3
int week = 週の番号 ;
week = FEB ; // エラーにはならないけど、意味的に間違い。

列挙型は、以下の様な使い方をすることで、シンプルな書き方で定数に意味をもたせることができる。このため、型の間違いの確認もできる。

enum Week {
   SUN , MON , TUE , ... SAT
} ;
enum Month {
   JAN , FEB , MAR , APR , MAY , ... DEC
} ;
enum Week week = 何らかの曜日 ;
if ( week == WED ) {
   水曜日の処理 ;
}
switch( week ) {
case MON : 月曜の処理 ; break ;
case WED : 水曜の処理 ; break ;
// この書き方をすると、月水以外の処理が記載されていない
// ことをコンパイラが警告してくれる。
}
week = FEB ; // エラー:週の変数に月の値を代入することはできない。