メインコンテンツ

フラッシュまたは位置付け呼び出しなしでのストリームからの入出力の交互実行

入出力ストリーム操作による未定義の動作

説明

この欠陥は、ファイル ストリームにおける入力操作の後に続く出力操作、または出力操作の後に続く入力操作が、操作の間でファイル ストリームの再位置付けが行われずに実行される場合に発生します。欠陥チェッカーは次のいずれかのファイル ポインターを使用して、ファイル操作における問題を検出します。

  • FILE* ポインター (fopen() などの関数を使用して開かれたファイルの場合)。

  • std::basic_filebuf オブジェクト (std::fstream() などの関数を使用して開かれたファイルの場合)

リスク

読み取りと書き込みの両方を目的にファイル ストリームを開く場合、ファイル ストリームにおける読み取り操作と書き込み操作の間で、中間的なストリームの再位置付け操作を実行する必要があります。

std::fstream() などの関数を使用してファイル ストリームを開くと、次の読み取りまたは書き込み操作が行われるファイル ストリーム内の位置を追跡するために、std::basic_filebuf オブジェクトが作成されます。このオブジェクトの実装には C FILE* 抽象化が使用されます。C 標準に従って、FILE* ポインターを使用して、ファイル ストリームにおける入力操作と出力操作の両方を実行できます。ただし、入力操作の後に出力操作を実行する場合 (またはその逆の場合)、その前に、ファイル ストリームでポインターを再位置付けする必要があります。そうしないと、動作が未定義になります。

修正方法

読み取りと書き込みの両方を目的にファイル ストリームを開く場合、ファイル ストリームにおける読み取り操作と書き込み操作の間で、中間的なストリームの再位置付け操作を実行する必要があります。

  • std::basic_filebuf オブジェクトの場合は、std::fstream<>::seekp()std::fstream<>::seekg() などのメソッドを使用してファイル ストリームを再位置付けできます。

  • FILE* ポインターの場合は、出力操作と入力操作の間で fseek()fsetpos() などのファイル位置付け関数を呼び出すことができます (または、出力操作とそれに続く入力操作の間で関数 fflush() を呼び出してバッファーをフラッシュできます)。

すべて展開する

#include <stdio.h>
#define SIZE20 20

void initialize_data(char* data, size_t s) {};
const char *temp_filename = "/tmp/demo.txt";

void func()
{
    char data[SIZE20];
    char append_data[SIZE20];
    FILE *file;

    file = fopen(temp_filename, "a+");
    if (file == NULL)
      {
        /* Handle error. */;
      }

    initialize_data(append_data, SIZE20);

    if (fwrite(append_data, 1, SIZE20, file) != SIZE20)
      {
        (void)fclose(file);
        /* Handle error. */;
      }
	/* Read operation after write without 
	intervening flush. */
    if (fread(data, 1, SIZE20, file) < SIZE20)  
      {
          (void)fclose(file);
          /* Handle error. */;
      }

    if (fclose(file) == EOF)
      {
        /* Handle error. */;
      }
}
        
      

この例では、ファイル demo.txt は読み取りおよび追加のために開かれています。fwrite() の呼び出し後に行われる、介在するフラッシュ操作なしでの fread() の呼び出しは未定義の動作です。

修正 — 読み取り操作の前に fflush() を呼び出す

データをファイルに書き込んだ後、fread() を呼び出す前にフラッシュ呼び出しを実行します。

#include <stdio.h>
#define SIZE20 20

void initialize_data(char* data, size_t s) {};
const char *temp_filename = "/tmp/demo.txt";


void func()
{
    char data[SIZE20];
    char append_data[SIZE20];
    FILE *file;

    file = fopen(temp_filename, "a+");
    if (file == NULL)
      {
        /* Handle error. */;
      }

    initialize_data(append_data, SIZE20);

    if (fwrite(append_data, 1, SIZE20, file) != SIZE20)
      {
        (void)fclose(file);
        /* Handle error. */;
      }
	/* Buffer flush after write and before read */
    if (fflush(file) != 0)  
      {
        (void)fclose(file);
        /* Handle error. */;
      }
    if (fread(data, 1, SIZE20, file) < SIZE20)
      {
        (void)fclose(file);
        /* Handle error. */;
      }

    if (fclose(file) == EOF)
      {
        /* Handle error. */;
      }
} 

結果情報

グループ: Programming
言語: C | C++
既定値: 手書きコードはオン、生成コードはオフ
コマンド ライン構文: IO_INTERLEAVING
影響度: Low

バージョン履歴

R2017b で導入

すべて展開する