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 байтов.