c ++ O3优化中断工作while循环

emr*_*duz 2 c++ fseek compiler-optimization c++11

我有这个简单的代码,它在 pcm wav 文件中使用 fseek 逐步搜索文件中的“数据”:

  FILE * waveFile;
  waveFile = fopen ( this->fileLocation.c_str ( ), "rb" );

  // ... some other code here between, then ... //

  int seekTo = 0;
  bool found = false;
  char data[4];

  rewind ( waveFile );
  while ( !found && ( fseek ( waveFile, seekTo, SEEK_SET ) == 0 )) {
    fread ( data, sizeof ( data ), 1, waveFile );
    if (( std::strcmp ( data, "data" ) == 0 ) || ( std::strcmp ( data, "Data" ) == 0 ) || ( std::strcmp ( data, "DATA" ) == 0 )) {
      found = true;
      fread ( &waveHeader->DATA_SIZE, sizeof ( waveHeader->DATA_SIZE ), 1, waveFile );
    }
    seekTo++;
  }
Run Code Online (Sandbox Code Playgroud)

代码工作正常,在测试文件上找到数据,读取剩余的数据。由于即使是最大的文件,“数据”也接近开头,因此这段代码对我来说没问题。

但是,当我添加 cpp 标志 -O3 时,代码会出错,而循环永远不会完成。

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O3")
Run Code Online (Sandbox Code Playgroud)

我正在使用 cmake + lldb (osx, clion),如果我使用 GDB,也会发生同样的事情。

可能是什么问题,我该如何解决?

附注。我不是要改进你看到的代码,我是想了解为什么编译器优化会破解这个 while 循环。

PSS。这是空终止的工作代码:

  int seekTo = 0;
  char data[5];

  rewind ( waveFile );
  while (( fseek ( waveFile, seekTo, SEEK_SET ) == 0 )) {
    fread ( data, 4, 1, waveFile );
    data[ 4 ] = '\0';

    if (( std::strcmp ( data, "data" ) == 0 ) || ( std::strcmp ( data, "Data" ) == 0 ) || ( std::strcmp ( data, "DATA" ) == 0 )) {
      fread ( &waveHeader->DATA_SIZE, sizeof ( waveHeader->DATA_SIZE ), 1, waveFile );
      break;
    }
    seekTo += 1;
  }
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 5

因为没有其他人想写一个答案......当代码在优化关闭的情况下工作但在优化打开的情况下停止工作时,很可能是编译器优化揭示了一些未定义的行为。在您的情况下,该错误是:

char data[4];
...
fread ( data, sizeof ( data ), 1, waveFile );
if (( std::strcmp ( data, "data" ) == 0 ) || ( std::strcmp ( data, "Data" ) == 0 ) || ( std::strcmp ( data, "DATA" ) == 0 )) {
Run Code Online (Sandbox Code Playgroud)

strcmp 是为了:

按字典顺序比较两个以null 结尾的字节字符串。

所以要么data碰巧\0在某个地方有一个,并且比较是错误的(因为data太短了)。或者它没有,你将data在内存中读取一些随机空字节的末尾。其结果是,编译器可以推断出,有没有办法是比较可能是真实和优化你的代码:

if (false) { ... }
Run Code Online (Sandbox Code Playgroud)

然后if完全删除该语句。

也许在未优化的构建中,您碰巧紧随其后总是内存为零data并且if从未优化过?


一个简单的解决方法是确保以data空值结尾:

char data[5];
data[4] = '\0';
// rest as before
Run Code Online (Sandbox Code Playgroud)

或者替换您对strcmpto的调用memcmp,提供sizeof(data)额外的长度参数。