C言語入門|int型データをバイナリとして読み書きする

前の記事では、char配列をバイナリデータとして読み書きする方法を学びました。
ここでは一歩進んで、int型の数値そのものをバイナリとしてファイルに保存し、再び読み取る方法を解説します。

数値データをバイナリで扱えるようになると、

  • セーブデータ
  • 設定ファイル
  • 高速な数値データの保存・復元

といった実用的な処理が書けるようになります。

ポイントはとてもシンプルで、
文字列と違って、int型はサイズが決まっているため、そのサイズ分をそのまま読み書きする
という考え方です。

サンプルプログラム:int型をバイナリとして読み書きする

次のプログラムは、

  1. int型変数に格納された数値をバイナリファイルに書き込み
  2. 同じファイルから int型データを読み取り
  3. 画面に表示する

という流れになっています。

サンプルプログラム

プロジェクト名:14-11-1 ソースファイル名: sample14-11-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE* fp;
    int wbuf = 100;   // 書き込みデータ
    int rbuf;        // 読み取り用データ

    /* --- バイナリ書き込み --- */
    if ((fp = fopen("try.dat", "wb")) == NULL) {
        printf("ファイルを開けませんでした\n");
        exit(1);
    }

    fwrite(&wbuf, sizeof(int), 1, fp);  // int型を1個書き込む
    fclose(fp);

    /* --- バイナリ読み取り --- */
    if ((fp = fopen("try.dat", "rb")) == NULL) {
        printf("ファイルを開けませんでした\n");
        exit(1);
    }

    int cn = fread(&rbuf, sizeof(int), 1, fp);  // int型を1個読み取る
    fclose(fp);

    printf("%d個のデータを読み取りました:%d\n", cn, rbuf);

    return 0;
}

実行結果

1個のデータを読み取りました:100

fwrite による int型データの書き込み

書き込み処理の中心は、次の1行です。

fwrite(&wbuf, sizeof(int), 1, fp);

この呼び出しを分解して考えてみましょう。

引数意味
第1引数書き込むデータの先頭アドレス
第2引数データ1個あたりのサイズ
第3引数書き込むデータの個数
第4引数ファイルポインタ

ここでは、

  • 書き込むデータは int型変数 wbuf
  • int型のサイズは sizeof(int) バイト
  • 個数は 1

となっているため、

wbuf が占めているメモリ領域を、そのままバイナリデータとして書き込む

という処理になります。

文字列とは違い、'\0' や改行といった概念は一切関係ありません。

fread による int型データの読み取り

読み取り処理も、考え方はまったく同じです。

int cn = fread(&rbuf, sizeof(int), 1, fp);

この処理では、

  • sizeof(int) バイト分のデータを
  • 1個分

読み取り、その結果を rbuf のメモリ領域に直接格納します。

戻り値 cn には、実際に読み取れたデータの個数が入ります。
今回のように正常に読み取れた場合は 1 になります。

なぜ & を付けているのか

char配列のときとは違い、int型の読み書きでは 必ず & を付けてアドレスを渡している点に注目してください。

fwrite(&wbuf, sizeof(int), 1, fp);
fread(&rbuf, sizeof(int), 1, fp);

これは、

  • 配列名は自動的に先頭アドレスになる。
  • 変数名は値そのものを表す。

という C言語のルールによるものです。

int型データをバイナリとして扱う場合は、

値ではなく、その値が置かれているメモリの場所を渡す

という考え方が重要になります。

char配列との違いを整理する

ここで、前節の char配列との違いを簡単にまとめておきましょう。

データ型第1引数に渡すもの'\0' の扱い
char配列配列名自分で付ける必要あり
int型&変数名不要

int型はサイズが固定されているため、
サイズ指定だけで完全に読み書きが完結するのが特徴です。

まとめ

  • int型は サイズが決まったバイナリデータ
  • fwrite と fread は サイズと個数で読み書きする
  • int型では必ずアドレスを渡す。
  • '\0' などの文字列特有の処理は不要

この考え方が理解できれば、
次は 配列や構造体をまとめてバイナリ保存する ことも自然に理解できるようになります。