Пара интересных багов
Dec 19, 2006 · Comments64bitОтладкаПрограммирование
На днях попытался собрать 64-битную версию Notepad2, благо исходники доступны. Надо сказать, что зачастую сборка изначально 32-битного приложения под x64 не доставляет никаких проблем, за исключанием множества предупреждений компилятора. Однако не в этот раз. Среди всех ошибок две показались наиболее интересными.
Первая проблема, в общем-то, не связана напрямую с 64-битностью. Одна из вспомогательных функций была объявлена следующим образом:
void RegDeleteKeyEx(HKEY, LPCSTR, BOOL*);
Её реализация представляла собой обертку вокруг RegDeleteKey, позволяющую удалять ключ реестра вместе со всеми подключами. Это работало, пока не вышла Window XP x64 и Vista. В Window XP x64 и Vista функциональность RegDeleteKey была расширена и новая версия функции получила имя “RegDeleteKeyEx”. Естественно, что две функции с одинаковым именем не смогли ужиться в одной программе (Notepad2 написан на C).
Этот пример хорошо показывает почему плохо называть обертки системных функций в стиле FooBarEx. С выходом новой версии системы в ней самой может появиться функция FooBarEx.
Вторая проблема иллюструриет тезис о вреде преждевременной оптимизации. Приложение падало сразу после запуска при вызове функции FormatString:
int FormatString(LPSTR lpOutput, int nOutput, UINT uIdFormat, ...)
{
...
wvsprintf(lpOutput, p, (LPVOID)(&uIdFormat; + 1)); // < -- bug
...
}
Само падение происходило где-то в глубинах wvsprintf, однако причина проблемы была в странной арифметике вокруг uIdFormat. Насколько я могу судить, автор пытался сэкономить пару строк кода и одну переменную, отказавшись от использования макроса va_start(). Однако в 64-х битном окружении выражения (&uIdFormat; + 1) и va_start(…) дают разный результат: первое сдвигает указатель на 4-е байта, второе – сдвигает и выравнивает указатель на границу 8-ми байт.