Не стоит недооценивать полезные советы

Posted: понедельник, 21 декабря 2009 г.

Буквально на днях я совершил невероятно глупую, но показательную ошибку. Начинающим в C++ я себя вроде как не считаю, и судя по всему, очень зря :)

Собственно, предыстория.

Служебный долг обязывает писать программы под Windows Mobile для ARM-процессоров. Занимаюсь я этим всего 3 недели, посему многие нюансы разработки под эту платформу мне доселе неизвестны, что в принципе не оправдывает сделанную ошибку.

Задача состояла в выполнении кое-каких математических вычислений. Я, не задумываясь, подключил <cmath> и с удивлением обнаружил, что в соответствующем заголовочном файле отсутствует объявление константы M_PI. При разработке для win32 соответствующая константа включается макросом _USE_MATH_DEFINES, однако, здесь ничего такого мне обнаружить не удалось. «Пораскинув мозгами», я пришёл к выводу, что объявление числа π отсутствует вследствие того, что FPU на ARM-устройствах нет (хотя причина вряд ли состоит в этом).

Недолго думая, я решил написать свой #define, добросовестно скопировав значение константы из стандартного калькулятора примерно вот так:

#define _PI 3,14159265358979323846

а использовал дальше вот таким образом:

// по сути, простой перевод в радианы
float someAngle = 45.0;
someAngle *= _PI / 180;
// ...

Visual Studio ничего не имела против и я со «спокойной» душой полчаса искал ошибку, так как результат вычислений естественно был неверным (оригинальный код, конечно же, сложнее, из-за чего при поиске ошибки моё внимание было сконцентрировано преимущественно на других нетривиальных местах).

А истина где-то рядом…

Каким-то невероятным образом чутьё мне всё-таки подсказало перепроверить #define, и я был шокирован :) Вся проблема была в том, что в моей Windows региональные стандарты были настроены в соответствии с принятыми в русских странах, то есть для отделения целой части от дробной в числах используется запятая, а не точка.

В C++ же, как всем известно, для этих целей используется точка, а запятая играет роль оператора. В отличие от той же Java, где использовать operator,() можно только внутри цикла for (если мне память не изменяет), C++ позволяет это делать где угодно.

Мораль

Когда в умных книгах пишут, что для объявления констант в C++ следует использовать ключевое слово const, а не препроцессорную директиву #define, это следует расценивать как правило, а не как совет. Использование const позволило бы выявить ошибку ещё на этапе компиляции, хотя… что я вам рассказываю — Вы и так это прекрасно знаете.

Что меня побудило использовать #define вместо const? Хм… Вероятно тот факт, что в стандартной библиотеке тоже используется препроцессорная директива, но мне и вам не следовало бы забывать, что <cmath> достался нам в наследство от языка C, где ключевое слово const попросту отсутствовало.

Будьте внимательны! :)

blog comments powered by Disqus