Пара интересных багов

На днях попытался собрать 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-ми байт.

comments powered by Disqus