Блог пользователя thematdev

Автор thematdev, история, 3 года назад, По-русски

Я заслал решение с трех разных компиляторов и получил три разных вердикта(OK, WA 1, ML 1) по задаче 1553E - Сдвиг перестановки с последнего раунда

123344376

123363613

123363479

Потом я запустил valgrind (g++ 11.1.0) и обнаружил странные "still reachable" адреса памяти, связанные с ios_base::sync_with_stdio. Потом я обнаружил, что он был в функции solve, которую я вызываю для каждого набора входных данных, а не в main, после чего я перенес его в main и получил OK на всех компиляторах.

valgrind output on sample

Достаточно странная , и самое главное, интересная ситуация, поэтому я прошу знающих людей подсказать в чём была проблема в комментариях. Также буду рад любой критике кода, особенно той, которая помогла бы в дальнейшем не выстреливать себе в ногу подобным образом.

  • Проголосовать: нравится
  • +53
  • Проголосовать: не нравится

»
3 года назад, # |
  Проголосовать: нравится +17 Проголосовать: не нравится

Скорее всего, дело в том, что ios_base::sync_with_stdio и cin.tie вызываются, во-первых, несколько раз, а во-вторых, после того, как из входных данных уже считано кол-во тестов. Из-за этого могло произойти неправильное считывание данных (других причин, по которым n считывается, а потом показывается как неинициализированное значение, я не вижу).

Обычно эти две "магические строки, ускоряющие ввод/вывод" вызывают один раз, до считывания/вывода.

  • »
    »
    3 года назад, # ^ |
      Проголосовать: нравится +25 Проголосовать: не нравится

    Судя по всему, дело именно в том, что сначала произошло считывание, а потом десинхронизация. cppreference утверждает, что если sync_with_stdio вызывается после считывания чего-то из входных данных, то поведение зависит от реализации компилятора: от отсутствия изменений до уничтожения буфера входных данных:

    If this function is called after I/O has occurred on the standard stream, the behavior is implementation-defined: implementations range from no effect to destroying the read buffer.

»
3 года назад, # |
  Проголосовать: нравится +28 Проголосовать: не нравится

The results are indeed very strange. However, the cppreference page for sync_with_stdio mentions the following:

"If this function is called after I/O has occurred on the standard stream, the behavior is implementation-defined: implementations range from no effect to destroying the read buffer."

In this case, the read buffer is probably destroyed in the GCC implementation (and somehow not in the MSVC one). As a result, the variables n and m don't get initialized properly from the call cin >> ..., and things go awry from there. Moreover, if you check the error flags in cin after unsyncing with stdio and then reading more input, you would find that cin.good() becomes false, whereas cin.eof() and cin.fail() become true. It means EOF is reached, so the buffered input from early is really gone. (See the reference page for basic_istream for more info.)

This is an implementation-defined territory, and I guess it's not that far from undefined behavior (which is a main source for common bugs in programming), so it might be a good idea to try to avoid dealing with these territories.