PVS-Studio - это инструмент для выявления ошибок в исходном коде программ, написанных на языках С, C++ и C#.
PVS-Studio выполняет статический анализ кода и генерирует отчёт, помогающий программисту находить и устранять ошибки. PVS-Studio выполняет широкий спектр проверок кода, но наиболее силён в поисках опечаток и последствий неудачного Copy-Paste. Показательные примеры таких ошибок: V501, V517, V522, V523, V3001.
Анализатор ориентирован на разработчиков, использующих среду Visual Studio, и может в фоновом режиме выполнять анализ измененных файлов после их компиляции. В идеале ошибки будут обнаружены и исправлены ещё до попадания в репозиторий. Однако ничто не мешает использовать анализатор для проверки всего решения целиком или для встраивания в системы непрерывной интеграции. Эти и иные способы использования анализатора описаны в документации.
PVS-Studio выполняет статический анализ кода и генерирует отчёт, помогающий программисту находить и устранять ошибки. PVS-Studio выполняет широкий спектр проверок кода, но наиболее силён в поисках опечаток и последствий неудачного Copy-Paste. Показательные примеры таких ошибок: V501, V517, V522, V523, V3001.
Анализатор ориентирован на разработчиков, использующих среду Visual Studio, и может в фоновом режиме выполнять анализ измененных файлов после их компиляции. В идеале ошибки будут обнаружены и исправлены ещё до попадания в репозиторий. Однако ничто не мешает использовать анализатор для проверки всего решения целиком или для встраивания в системы непрерывной интеграции. Эти и иные способы использования анализатора описаны в документации.
О преобразовании типов в арифметических выражениях в C++ и C#
Запись от el_programmer размещена 29.03.2016 в 12:43
Обновил(-а) tezaurismosis 19.06.2016 в 19:48 (Рекламные ссылки удалены)
Обновил(-а) tezaurismosis 19.06.2016 в 19:48 (Рекламные ссылки удалены)
Метки c++, coding, csharp, programming, pvs-studio
Автор: Илья Иванов В арифметическом выражении типы операндов могут быть преобразованы к общему типу. Такие преобразования описаны в стандарте языка - в C# они существенно проще чем в C++. Тем не менее, скорее всего далеко не каждый программист знает обо всех тонкостях. Возможно у вас были случаи, когда тип арифметического выражения оказывался не таким как вы предполагали? Насколько хорошо вы знаете стандарт языка? Предлагаю проверить себя, заменив [i]auto [/i]и [i]var [/i]на правильный тип в этих выражениях и заодно вычислить результат: C++ (будет считать, что используется модель данных LP64): [CPP]void Test() { unsigned char c1 = std::numeric_limits<unsigned char>::max(); unsigned char c2 = std::numeric_limits<unsigned char>::max(); int i1 = std::numeric_limits<int>::max(); int i2 = std::numeric_limits<int>::max(); unsigned int u1 = std::numeric_limits<unsigned int>::max(); auto x = c1 + c2; auto y = i1 + i2; auto z = i1 + u1; } [/CPP] C#: [CSHARP]void Test() { byte b1 = byte.MaxValue; byte b2 = byte.MaxValue; int i1 = int.MaxValue; int i2 = int.MaxValue; uint u1 = uint.MaxValue; var x = b1 + b2; var y = i1 + i2; var z = i1 + u1; } [/CSHARP] Ответ ниже С++ (LP64): [CPP] int x = c1 + c2; // = 510 int y = i1 + i2; // = -2 unsigned int z = i1 + u1; // = 2147483646 [/CPP] C#: [CSHARP] int x = b1 + b2; // = 510 int y = i1 + i2; // = -2 long z = i1 + u1; // = 6442450942 [/CSHARP] Из этого теста, а если точнее, из стандартов языка С++ и C# следует: [b]1. Вычисление [/b][b][i]x[/i][/b][b]. [/b]В арифметическом выражении все переменные, значения которых представимы типом [i]int[/i], будут преобразованы к типу [i]int[/i]. Поэтому при сложении двух переменных типа [i]char[/i], [i]unsigned char[/i], [i]short int[/i], [i]unsigned short int[/i] в C++ или переменных типа [i]byte[/i], [i]sbyte[/i], [i]short[/i], [i]ushort [/i]в C# результат будет иметь тип [i]int [/i]и переполнения не произойдёт. В наших примерах переменная [i]x [/i]примет значение 510. [b]2. Вычисление [/b][b][i]y[/i][/b][b]. [/b]Если оба аргумента имеют тип [i]int [/i]дальнейшего расширения типов происходить уже не будет и здесь уже возможно переполнение. В C++ это неопределённое поведение. В C# по умолчанию в случае переполнения приложение продолжит работу. Используя ключевое слово checked или флаг компилятора /checked можно изменить поведение приложения чтобы в случае переполнения бросался OverflowException. В нашем тесте переменная [i]y [/i]примет значение -2 и в C++ и в C#. Хотя ещё раз повторю, что в С++ мы имеем неопределённое поведение, результом которого может быть что угодно, например в [i]y [/i]может быть записано число 100500 или произойдёт переполнение стека. [b]3. Вычисление [/b][b][i]z[/i][/b][b]. [/b]Если один из аргументов имеет тип [i]int[/i], а другой [i]unsigned int[/i]в C++ или [i]uint [/i]в C#, то здесь стандарты двух языков написаны по-разному! В С++ оба аргумента будут преобразованы к типу [i]unsigned int[/i], кстати здесь уже не будет неопределённого поведения при переполнении. В C# оба аргумента будут преобразованы к типу [i]long [/i]и переполнения не произойдёт ни при каких условиях. Поэтому мы и получили в наших программах на разных языках, разные значения для переменной [i]z[/i]. Рассмотрим теперь какие ошибки может содержать код, который написан без учёта правильного преобразования типов. Пример на С++: [CPP]typedef unsigned int Ipp32u; typedef signed int Ipp32s; Ipp32u m_iCurrMBIndex; VC1EncoderMBInfo* VC1EncoderMBs::GetPevMBInfo(Ipp32s x, Ipp32s y) { Ipp32s row = (y > 0) ? m_iPrevRowIndex : m_iCurrRowIndex; return ((m_iCurrMBIndex - x < 0 || row < 0) ? 0 : &m_MBInfo[row][m_iCurrMBIndex - x]); } [/CPP] Это пример кода из проекта IPP Samples. При сравнении результата выражения с нулём нужно помнить о том что [i]int [/i]может быть преобразован в [i]unsigned int[/i], а [i]long[/i] - в [i]unsigned long[/i]. В нашем случае выражение [i]m_iCurrMBIndex[/i] - [i]x[/i] будет иметь тип [i]unsigned int[/i], и поэтому оно всегда неотрицательно, о чём сообщит PVS-Studio: V547 Expression 'm_iCurrMBIndex - x < 0' is always false. Unsigned type value is never < 0. Пример на C#: [CSHARP]public int Next(int minValue, int maxValue) { long num = maxValue - minValue; if (num <= 0x7fffffffL) { return (((int)(this.Sample() * num)) + minValue); } return (((int)((long)(this.GetSampleForLargeRange() * num))) + minValue); } [/CSHARP] Это пример из проекта SpaceEngineers. В C# нужно помнить о том, что при сложении двух переменных типа [i]int [/i]расширения типа до [i]long [/i]не произойдёт в отличие от сложения переменной типа [i]int [/i]и переменной типа [i]uint[/i]. Поэтому здесь в переменную [i]num [/i]будет записано значение типа [i]int[/i], которое всегда удовлетворяет условию [i]num[/i] <= 0x7fffffffL. PVS-Studio знает об этом и выдаёт сообщение V3022 Expression 'num <= 0x7fffffffL' is always true. Хорошо, когда знаешь стандарт и не допускаешь подобные ошибки, но на практике постоянно помнить все тонкости языка сложно, а в случае с C++ вообще нереально. Поэтому полезно пользоваться статическими анализаторами, например, PVS-Studio. [size=5]Дополнительные ссылки[/size] [list=1][*][url=http://en.cppreference.com/w/cpp/language/operator_arithmetic]Преобразование типов в арифметических выражениях в C++[/url].[*][url=https://msdn.microsoft.com/en-us/library/aa691330.aspx]Преобразование типов в арифметических выражениях в C#[/url].[/list] |
Всего комментариев 1
Комментарии