C++ O3 оптимизация прерывает работу цикла while

У меня есть этот простой код, который ищет файл для "данных" шаг за шагом, используя fseek в wcm файле pcm:

  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++;
  }

Код работает правильно, и в тестовых файлах он находит данные, читает остальные. Поскольку "данные" близки к началу даже для самых больших файлов, этот код подходит мне.

Но когда я добавляю флаг cpp -O3, код становится бесполезным, а цикл никогда не заканчивается.

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O3")

Я использую cmake + lldb (osx, clion), то же самое происходит, если я использую GDB.

В чем может быть проблема, как я могу решить эту проблему?

PS. Я не пытаюсь улучшить код, который вы видите, я пытаюсь понять, почему оптимизация компилятора взламывает этот цикл 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;
  }

2 ответа

Решение

Поскольку никто больше не хочет писать ответ... Когда код работает с отключенными оптимизациями, но перестает работать с включенными оптимизациями, это, вероятно, некое неопределенное поведение, которое выявляется оптимизацией компилятора. В вашем случае эта ошибка:

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 )) {

strcmp для:

Лексикографически сравнивает две строки байтов с нулевым символом в конце.

Так что либо data случается иметь \0 в этом где-то и сравнение ложное (потому что data было бы слишком коротким). Или это не так, и вы будете читать далеко от конца data к некоторому случайному нулевому байту в памяти. В результате компилятор может сделать вывод, что сравнение не может быть верным, и оптимизировать ваш код:

if (false) { ... }

а затем бросьте if заявление полностью.

Возможно, в неоптимизированной сборке у вас всегда была нулевая память сразу после data и if никогда не был оптимизирован?


Это легко исправить, если бы data заканчивается нулем:

char data[5];
data[4] = '\0';
// rest as before

Или заменить ваши звонки strcmp в memcmp, предоставляя sizeof(data) в качестве дополнительного аргумента длины.

strcmp - это функция сравнения строк, которая сравнивает строки, пока не будет найден символ NUL. Вы используете char[4] для вашей строки, поэтому нет места для символа NUL. Тот факт, что это сработало, был случайностью.

В вашем случае вам лучше использовать memcpy для 4 байтов.

Другие вопросы по тегам