О Преобразовании Типов В Арифметических Выражениях В C++ И C#

В арифметическом выражении типы операндов могут быть преобразованы в общий тип.

Подобные преобразования описаны в стандарте языка — в 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; }

Ответ под картинкой

О преобразовании типов в арифметических выражениях в C++ и C#

С++ ( ЛП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-Студия .



Дополнительные ссылки

  1. Преобразование типов в арифметических выражениях в C++ .

  2. Преобразование типов в арифметических выражениях в C# .



О преобразовании типов в арифметических выражениях в C++ и C#

Если вы хотите поделиться этой статьей с англоязычной аудиторией, воспользуйтесь ссылкой для перевода: Илья Иванов.

Преобразование типов в арифметических выражениях C++ и C# .

Вы прочитали статью и у вас есть вопросы? Часто о наших статьях задают одни и те же вопросы.

Ответы на них мы собрали здесь: Ответы на вопросы читателей статей о PVS-Studio версии 2015 .

Пожалуйста, проверьте список.

Теги: #C++ #статический анализ кода #pvs-studio #статический анализ кода #преобразование типов

Вместе с данным постом часто просматривают: