構造体の説明の後半として、メモリの使用量と関係深いワード境界とビットフィールドの説明を行う。 最初に、主記憶の不足とプログラムの関係として、仮想メモリとメモリ不足時にハードディスクアクセスが多発し、スピード低下について述べる。
ワード境界
struct A { char name[ 3 ] ; // イニシャルと点数の構造体? int point ; } a[ 4 ] ; 境界無視 境界配置 |NNNP| |NNNx| N:name |PPPN| |PPPP| P:point |NNPP| |NNNx| |PPNN| |PPPP| |NPPP| |NNNx| |PNNN| |PPPP| |PPPP| |NNNx| |PPPP|
CPUに比べて、主記憶の速度は遅いため、メモリアクセスは必要最小限にしたい。 しかしながらワード境界を無視すると、a[0].point の取得には、2回のメモリアクセスが 発生するため、処理速度が低下する場合がある。 このため、name 3文字の後ろに1byteの空きを設けて、ワード境界をまたがらない様に 構造体の要素を配置するのが一般的。
この話の前に、char=8bit=1byte=0..255(-128..0..127),32bitCPUなら、 int=32bit=4byte=-2^31..0..2^31-1といった復習を簡単に行った。
メモリインタリーブとよばれる方式を使うと、 ワード境界があっても最小限のメモリ参照で済むが、ハードウェアが複雑化する。 情報処理技術者試験を受けるのであれば、インターリーブも理解しておくこと。
ビットフィールド
struct YMD { int year ; int month ; int day ; } ;
といった構造体では、12byte のメモリを使用するけど、メモリ使用を減らすには、 year=0..2047,month=0..15,day=0..31と考えれば、20bitで十分。 メモリの使用量を減らすために、year , month , day を1つのint型で覚えるには?
ymd = year *10000 + month*100 + day ; printf( "月=%d" , (ymd % 10000) / 100 ) ;
という方法もあるけど、2進数として年月日を覚えるのであれば、
// YYYYYYYYYYY000000000 // or MMMM00000 // or DDDDD ymd = (year << 9) | (month << 5) | day ; printf( "年=%d" , ymd >> 9 ) ; printf( "月=%d" , (ymd >> 5) & 0x0F ) ; printf( "日=%d" , ymd & 0x1F ) ;
とすれば、int 型で、年月日を必要最小限のbit数で保存できる。 しかしながら、2進数演算は分かりにくいので、以下のようなビットフィールドを使えば簡単。
struct YMD { unsigned int year : 11 ; unsigned int month : 4 ; unsigned int day : 5 ; } ;