Почему компилятору не нравится неявное приведение к типу uint? - PullRequest
3 голосов
/ 14 мая 2009

Я столкнулся с парой подобных причуд в отношении использования uint как в C ++, так и в C #, и теперь я задаюсь вопросом о причинах (которые могут быть совершенно разными для каждого примера). Для обоих примеров обратите внимание, что я компилирую с уровнями предупреждения, установленными на максимум.

(1) gcc жалуется на сравнение int с uint в следующем, тогда как vc ++ этого не делает:

uint foo = <somevalue>;
if( foo == ~0 )  //right here
   ...

Сравнение с 0 - это нормально, без приведения на gcc и vc ++.

(2) В C # 3.5 я столкнулся с похожей проблемой. Следующее работает отлично:

uint foo = 1;
uint bar = 2;

Но это выдает предупреждение uint / int:

bool condition = <somevalue>;
uint foo = condition ? 1 : 2; //right here

Что дает, почему компилятор так чувствителен к подписи непосредственных значений? Я полностью понимаю проблему при назначении из переменных, но это не имеет смысла для непосредственных значений; Есть ли какая-то скрытая трудность в разборе, которая не позволяет этому поведению быть разрешенным? Или что?

Редактировать: Да, я знаю, что могу добавить свои числа к суффиксу 'u', но это обходит мой вопрос, который составляет примерно неявно приведение к левой стороне, явно не используется правая сторона.

Ответы [ 7 ]

7 голосов
/ 14 мая 2009

Смешивание значений со знаком и без знака без явного намерения программиста может привести к незначительным ошибкам. Это правда, что int и uint хранятся в ячейках памяти одинакового размера (4 байта) и совместимы с назначением местоположения, но их битовое представление и поведение в отношении общих операций различны, а также имеют разные диапазоны.

Это все равно что решить математическую задачу и сказать, почему я не могу свободно поменять интервал [-2147483648 на 2147483647] с интервалом [0 на 4294967295]? Ну, вы можете, но если вы выходите за пределы, результаты могут быть не правильными :). Вот почему компилятор запрашивает ваше подтверждение (будучи явным), что вы не смешиваете разные типы по ошибке.

Также в C # литеральные числа всегда являются int32, если вам нужен какой-то другой литеральный тип, такой как float, decimal, ulong и т. Д., Вы должны использовать соответствующий суффикс, в вашем случае:

uint foo = condition ? 1u : 2u; // uint literals;

Редактировать: Как указывает Эндрю Хэйр, целочисленные литералы C # не только int32, но (int, uint, long, ulong) в зависимости от размера, как описано здесь:

C # Inger literal - MSDN

4 голосов
/ 14 мая 2009

Я не могу говорить за gcc, но что касается компилятора C # 3, вам нужно явно сказать ему, что эти ints должны быть без знака:

uint foo = condition ? 1U : 2U;

Компилятор C # любит ints и предполагает, что все интегральные значения в диапазоне ints. Так как в вашем выражении используется условный оператор, компилятор слишком хочет предположить, что ваши литеральные значения равны ints, и тогда присвоение завершится неудачей.


Редактировать: Обратите внимание, что я сказал интегральные значения , которые в пределах диапазона от System.Int32. Рассмотрим этот пример:

using System;    

class Program    
{    
    static void Main()    
    {    
        Console.WriteLine(1.GetType());    
        Console.WriteLine(2147483648.GetType());    
    }    
}

Вывод:

System.Int32
System.UInt32

1 голос
/ 14 мая 2009

Позвольте мне задать вам вопрос ... предположим, у вас есть 32-разрядный компьютер с прямым порядком байтов, который представляет числа в дополнении 2. Какое значение типа unsigned int равно значению 0xFFFFFFFF со знаком int? Хорошо, не могли бы вы предупредить, если бы видели, как кто-то делает это?

0 голосов
/ 14 мая 2009

Я поддерживаю Поп-Каталина в том, что касается фактического ответа.

Я хотел бы добавить, если можно, что оператор static_cast <>, введенный в C ++, приводит к двоичному файлу, который аналогичен тому, который создается спецификатором типа. То есть попробуйте сравнить и сопоставить использование:

int abc = 123;
uint i = static_cast<unit>(abc); // C++ explicit cast
uint ii = 123U; // specifier
uint j = (uint)abc; // C-style cast

Мне нравится явное приведение, потому что оно помогает, когда вы возвращаетесь к коду в поисках скрытых ошибок приведения.

0 голосов
/ 14 мая 2009

Что касается вашего вопроса C ++, литерал 0 по умолчанию является знаком int. Выражение ~0 создает представление в виде двойного комплимента -1 (все биты включены). Похоже, компилятор жалуется, что вы пытаетесь сравнить unsigned int с -1.

Тем не менее, я не смог воспроизвести вашу ошибку на g ++ 4.0 или 4.2.

0 голосов
/ 14 мая 2009

При назначении значений переменным компилятор C # выполняет естественное неявное преобразование во время компиляции. Однако он может применить преобразование, только если у него достаточно контекста, чтобы вывести требуемый тип данных. Например:

uint foo = 1;

... работает, потому что значение 1 присваивается переменной известного типа. Тем не менее

uint foo = condition ? 1 : 2;

не работает, потому что тип данных не может быть выведен. При разборе исходного кода компилятор сравнивает 1 и 2 друг с другом, чтобы убедиться, что они одного типа. Из этого присваивания нельзя сделать вывод, поскольку это отдельное выражение.

0 голосов
/ 14 мая 2009

По умолчанию, когда вы набираете 0, или 1, или любое число, оно считается целым. Сравнение int с и uint небезопасно по понятным причинам, а что если ваш int меньше 0.

...