В арифметическом выражении типы операндов могут быть преобразованы в общий тип.
Подобные преобразования описаны в стандарте языка — в C# они гораздо проще, чем в C++.
Однако, скорее всего, не каждый программист знает обо всех тонкостях.
Возможно, у вас бывали случаи, когда тип арифметического выражения оказывался не таким, как вы ожидали? Насколько хорошо ты знаешь языковой стандарт? Предлагаю проверить себя, заменив авто И вар к правильному типу в этих выражениях и одновременно вычисляем результат: C++ (будем считать, что используется модель данных ЛП64 ):
С#: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; }
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;
}
Ответ под картинкой
С++ ( ЛП64 ):
int x = c1 + c2; // = 510
int y = i1 + i2; // = -2
unsigned int z = i1 + u1; // = 2147483646
С#:
int x = b1 + b2; // = 510
int y = i1 + i2; // = -2
long z = i1 + u1; // = 6442450942
Из этого теста, а точнее из стандартов языков C++ и C#: 1. Расчет Икс .
В арифметическом выражении все переменные, значения которых представимы типом интервал , будет преобразовано в тип интервал .
Поэтому при добавлении двух переменных типа голец , беззнаковый символ , короткий интервал , беззнаковое короткое целое число в C++ или введите переменные байт , сбайт , короткий , сокращать в C# результат будет типа интервал и перелива не будет. В наших примерах переменная Икс примет значение 510. 2. Расчет й .
Если оба аргумента имеют тип интервал дальнейшего расширения типов больше не произойдет и здесь уже возможно переполнение.
В C++ это неопределенное поведение.
В C# по умолчанию при возникновении переполнения приложение продолжает работать.
Используя ключевое слово «проверено» или флаг компилятора /checked, вы можете изменить поведение приложения так, чтобы в случае переполнения выдавалось исключение OverflowException. В нашем тесте переменная й примет значение -2 как в C++, так и в C#.
Хотя еще раз повторю, что в C++ мы имеем неопределенное поведение, результатом которого может быть что угодно, например в й может быть записано число 100500 или произойдет переполнение стека.
3. Расчет я .
Если один из аргументов имеет тип интервал , и другие беззнаковое целое число на С++ или uint в C# то стандарты двух языков написаны по-разному! В C++ оба аргумента будут приведены к типу беззнаковое целое число , кстати, неопределенного поведения при переполнении больше не будет. В C# оба аргумента будут приведены к типу длинный и переполнение не произойдет ни при каких обстоятельствах.
Именно поэтому в наших программах на разных языках мы получили разные значения переменной.
я .
Давайте теперь рассмотрим, какие ошибки может содержать код, написанный без учета корректного преобразования типов.
Пример на С++: 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]);
}
Это пример кода из проекта IPP Samples. Сравнивая результат выражения с нулем, нужно помнить, что интервал можно преобразовать в беззнаковое целое число , А длинный - В беззнаковый длинный .
В нашем случае выражение m_iCurrMBIndex — Икс будет иметь тип беззнаковое целое число , и поэтому оно всегда неотрицательно, о чем сообщит PVS-Studio: В547 Выражение 'm_iCurrMBIndex - x < 0' is always false. Unsigned type value is never < 0.
Пример на С#: 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);
}
Это пример из проекта SpaceEngineers. В C# нужно помнить, что при добавлении двух переменных типа интервал введите расширения для длинный не произойдет, в отличие от добавления переменной типа интервал и введите переменную uint .
Поэтому здесь в переменной число будет записано значение типа интервал , что всегда удовлетворяет условию число <= 0x7fffffffL. PVS-Studio knows about this and issues a message В3022 Выражение 'номер <= 0x7ffffffL' is always true. Хорошо, когда знаешь стандарт и не допускаешь таких ошибок, но на практике постоянно помнить все тонкости языка сложно, а в случае с C++ это и вовсе нереально.
Поэтому полезно использовать статические анализаторы, например, PVS-Студия .
Дополнительные ссылки
- Преобразование типов в арифметических выражениях в C++ .
- Преобразование типов в арифметических выражениях в C# .
Если вы хотите поделиться этой статьей с англоязычной аудиторией, воспользуйтесь ссылкой для перевода: Илья Иванов.
Преобразование типов в арифметических выражениях C++ и C# .
Вы прочитали статью и у вас есть вопросы? Часто о наших статьях задают одни и те же вопросы.
Ответы на них мы собрали здесь: Ответы на вопросы читателей статей о PVS-Studio версии 2015 .
Пожалуйста, проверьте список.
Теги: #C++ #статический анализ кода #pvs-studio #статический анализ кода #преобразование типов
-
Хронология Футурико — 404
19 Oct, 24 -
Библиотека Python Для Photon Server
19 Oct, 24 -
Плата За Удобство Использования
19 Oct, 24