понедельник, 5 сентября 2011 г.

[prog.bugs] Вернусь к незакрытому обсуждению бага в C++ном коде

Неожиданно обнаружил в почте комментарий к старой теме “А жуки все лезут и лезут. На этот раз приплюснутые”. К сожалению, тогда почему-то не смог ответить. А жаль, тема хорошая. Поэтому возвращусь к ней еще раз.

Итак, одна из проблем была вызвана кодом:

std::string
hex_char( char ch )
{
   char v[5];
   std::sprintf( v, "%02x", ch );
   return std::string(v);
}

Напомню суть проблемы. По умолчанию char в С++ – это знаковый тип (хотя это зависит от ключей компилятора). Поэтому, если в ch содержится не ASCII символ из диапазона [128, 255], то при передаче в sprintf будет получен отрицательный int. Шестнадцатиричное представление которого никак не уместится в буфер v (по крайней мере для int-ов, чья размерность больше 8 бит).

В тогдашних комментариях звучала мысль, что я зря обозвал этот баг приплюснутым. Это типичная C-шная ошибка – неверное указание размера буфера-приемника для sprintf (как и использование sprintf вместо snprintf, а еще лучше C++ных потоков вместо функций printf-ного семейства).

Посему объясняю свою точку зрения. Проблемы с sprintf – это уже следствие главной проблемы. А главная проблема в том, что разработчик забыл об особенностях C++ со знаковостью типа char. И, поскольку в C++ эта особенность присутствует изначально и человек пишет только на C++, то это именно проблема C++. То, что С++ унаследовал ее из C уже не имеет значения (хотя я C не знаю в достаточной степени, поэтому не уверен в наличии в нем заморочек на счет signed char, unsigned char).

Более того, я уверен, что человек, забывший об особенностях знака в типе char, отгреб бы гораздо больших проблем и с применением C++ных потоков. Вот, смотрим простейший код:

#include <iostream>

int
main()
   {
      char c = -1;

      std::cout << '\'' << std::hex
            << static_cast< unsigned int >(c) << '\'' << std::endl;
   }

Что мы увидим, запустив такую программу? ‘ff‘? А нетушки! Visual C++ и Intel C++ выдают `ffffffff`. Что совершено логично.

Но такой неправильный вывод, на самом-то деле, гораздо хуже аварийного завершения программы (по крайней мере во многих случаях). Представьте, что такое неправильное формирование hex-представление используется для сохранения в БД значения MD5 хеша или для передачи этого же хеша в XML-документе. Ведь ошибки не произойдет, hex-представление будет получено и уйдет куда-то дальше. Где со временем обязательно возникнут проблемы. Но ведь это же уже чужие проблемы ;)

Вот так я смотрю на это дело. И хорошо, что ошибка проявилась так жестко – падением программы. Ведь все-таки лучше сломаться, чем произвести неправильный результат. Fail fast, как говорится ;)

Отправить комментарий