【C言語】バイナリーデータ作成は明確な意思を持つべき

 普段は高レベルなプログラミングをしている人も、業務によっては低レベルなプログラムをすることもあります。近年ではXMLやJSONなどのテキストベースのデータ交換手法や、データベースエンジンへの書き込みでデータのやり取りをするので、そもそもの概念がない人もいます。


データ交換に明確な意思は必要

はるか昔ですが、C言語で整数型のデータをバイナリーデータとしてディスクに保管するサブシステムを作成しました。

仕様は先輩が作っていたのだが、そのときに整数型の順番は決まっているが、どのようにバイナリー化するかが決まっていませんでした。

ビッグエンディアンか?リトルエンディアンか?

気持ち悪いので先輩に聞き返したら。「それぞれ4バイトなのだから順番に入れれば良い」と言われました。それ以上の事は言いませんでした。当然リトルエンディアンなのかわかりません。

とりあえず、ビッグエンディアンとしてプログラムを作成しましたが、後で驚く発言を聞きました。

整数は上位下位が反転するのが常識です

これが、先輩の指示でした。まあ、ひどすぎです。確かにIntel系CPUだと、int型の整数をそのままメモリーダンプすれば、上位下位は逆転します。確かにそのとおりですが、なってしまうって指示は酷いです。

仕様を作るときは、決して「なってしまう」などという表現はしません。明確に行います。この場合なら

  • 整数は4バイトのリトルエンディアンで出力する
  • 実行される環境は全てWindowsでIntelのCPUである
  • 今回はビッグエンディアンの環境では実行されない
  • 今回はパフォーマンスを優先するため、ソフトウェアでの処理は見送る
  • 今回はSUNなどのビッグエンディアンの環境への移植性は考えない

というくらいの指示が必要です。

C言語での実験

#include<stdio.h>

//int型の動きを知るプログラム
main(){

unsigned char buf[10]; //とりあえず10バイト分のバッファを確保
int *iData; //整数型のポインタを宣言

//10バイト分のバッファを全て255で埋める
for(int i=0;i<10;i++){
buf[i]= 255;
}
//整数型のポインタのアドレスをバッファの先頭に設定する
iData = (int *)buf;
//整数型ポインタのアドレスに1000を代入
*iData = 1000;

//バッファの中身を表示
for(int i=0;i<10;i++){
printf("%d\n",buf[i]);

//本プログラムの狙いとしては、全てのバッファを埋めた後、同じアドレスに整数を代入する。
//バッファーが255で無い場合は、整数型の代入によって影響を受けたということになる。
//観察ポイントとしては、255じゃない部分の数が整数型のバイト数。
//数字が昇順であればintel系CPU,じゃなければ他のCPUということになる
//メモリーマッピングをする場合は、この方法は望ましくない。積極的に仕様を決めるべきである。
}
}

以上のコードを実験のために作りました。整数型の変数をメモリにダンプするとどうなるかの確認です。

仮に10バイトの領域を確保し255のデータで埋めます。次に、整数型のポインタを宣言し、そのアドレスを先程の10バイトの領域の先頭に設定します。

そして、その整数型のアドレスに1000を書き込みます。

これを私の環境 Windows11 で実行すると 先頭が232 次が3,次が0、次も0,そして後は255が続きます。

実行結果、最初の4バイトが整数として使われている

これが意味するところは

整数型を代入すると最初の4バイトに値が書き込まれる。先頭が232,次が3ということは収納はリトルエンディアンであるということです。

C言語でプログラムを組んでいる人は、常にこのような実験コードを動かして、ハードウェアの特性を把握しておくことが必要です。

ソースコード

繰り返しますが、「リトルエンディアンになってしまう」という意識でプログラムは組まないで下さい。「リトルエンディアンにする」、「ビッグエンディアンにする」という明確な意思と、妥協するのであれば考えた上で妥協する必要があります。

ウィンドウズアプリ入門 クリップボードにコピーされたらメッセージを受信する方法

 Windowsアプリでコピーされたときにイベントを取得できると便利です。例えばコピーされた内容を画面に表示したい場合などです。 もちろんタイマーイベントで定期的にポーリングをする方法もありますが、やや無駄な処理かもしれません。 Windowsとしてはコピーされたときのイベントを...