2012-04-08 3 views
19

Раньше сегодня я пытался добавить два указателя, и я заметил, что мне нужно вернуть результат обратно в ushort. Я думал, что это могло бы стать uint (чтобы предотвратить возможное непреднамеренное переполнение?), Но, к моему удивлению, это был int (System.Int32).Почему ushort + ushort равно int?

Есть ли какая-то умная причина для этого или это возможно, потому что int рассматривается как «основной» целочисленный тип?

Пример:

Edit: Как Gregs»ответ говорит, С # спецификация говорит, что оба операнда (в данном примере„а“и„б“) следует преобразовать в целое. Меня интересует основополагающая причина, почему это часть спецификации: почему спецификация C# не позволяет операциям напрямую использовать значения ushort?

+3

http://bytes.com/topic/c-sharp/answers/256857-does-ushort-ushort-return-int-value (см. Ответ J.Skeets) –

+0

Я обновил свой ответ из спецификаций C# от Microsoft – Habib

ответ

42

Простой и правильный ответ «потому что Спецификация языка C# так говорит».

Очевидно, вы не удовлетворены этим ответом и хотите знать «почему это так». Вы ищете «надежные и/или официальные источники», это будет немного сложно. Эти проектные решения были сделаны давно, 13 лет - много жизни собак в разработке программного обеспечения. Они были сделаны «старыми таймерами», как их называет Эрик Липпер, они перешли к более крупным и лучшим вещам и не публикуют ответы здесь, чтобы предоставить официальный источник.

Это может быть выведено, однако, с риском просто быть заслуживающим доверия. Любой управляемый компилятор, такой как C#, имеет ограничение на то, что ему нужно сгенерировать код для виртуальной машины .NET. Правила для которых тщательно (и вполне читаемы) описаны в спецификации CLI. Это спецификация Ecma-335, вы можете скачать ее бесплатно from here.

Перейдите к разделу 3.1, главе 3.1 и 3.2. Они описывают две инструкции IL, доступные для выполнения добавления, add и add.ovf. Нажмите ссылку на таблицу 2 «Двоичные числовые операции», в которой описаны, какие операнды допустимы для этих инструкций IL. Обратите внимание, что здесь есть всего несколько типов. байтовый и короткий, а также все неподписанные типы отсутствуют. Допускается только int, long, IntPtr и с плавающей запятой (float и double). С дополнительными ограничениями, отмеченными x, вы не можете добавить int к длинному, например.Эти ограничения не являются полностью искусственными, они основаны на том, что вы можете сделать достаточно эффективно на доступном оборудовании.

Любой управляемый компилятор должен иметь дело с этим, чтобы генерировать действительный IL. Это не сложно, просто преобразуйте ushort в более крупный тип значения, который находится в таблице, преобразование, которое всегда действует. Компилятор C# выбирает int, следующий более крупный тип, который появляется в таблице. Или, вообще говоря, конвертируйте любой из операндов в следующий наибольший тип значения, чтобы они оба имели один и тот же тип и отвечали ограничениям в таблице.

Теперь возникает новая проблема, проблема, с которой программисты на C# довольно ошеломлены. Результат добавления относится к продвинутому типу. В вашем случае это будет int. Таким образом, добавление двух значений ushort, скажем, 0x9000 и 0x9000, имеет вполне допустимый результат int: 0x12000. Проблема в том, что это значение, которое не укладывается в пользовательское пространство. Значение переполнено. Но он не сделал переполнения в расчете IL, он переполняется только тогда, когда компилятор пытается втиснуть его обратно в ushort. 0x12000 усекается до 0x2000. Загадочная другая ценность, которая имеет смысл только при подсчете с 2 или 16 пальцами, а не с 10.

Примечательно, что команда add.ovf не справляется с этой проблемой. Это инструкция для автоматической генерации исключения переполнения. Но это не так, фактический расчет на преобразованных ints не переполнялся.

Это то, где вступает в игру реальное дизайнерское решение. Похоже, старожилы решили, что просто усечение результата int для ushort было ошибкой. Это определенно. Они решили, что вы должны признать, что вы знаете, что добавление может переполняться и что все в порядке, если это произойдет. Они сделали вашей проблемой, главным образом потому, что они не знали, как сделать это и все еще генерировать эффективный код. Вы должны бросить. Да, это безумие, я уверен, что вы тоже не хотели этой проблемы.

Совершенно очевидно, что разработчики VB.NET приняли другое решение проблемы. Они действительно сделали своей проблемой и не пропустили доллар. Вы можете добавить два UShorts и назначить их в UShort без трансляции. Разница в том, что компилятор VB.NET фактически генерирует дополнительно IL, чтобы проверить условие переполнения. Это не дешевый код, делает каждое короткое дополнение примерно в 3 раза медленнее. Но в остальном причина, объясняющая, почему Microsoft поддерживает два языка, которые в ином случае имеют схожие возможности.

Короче говоря: вы платите цену, потому что используете тип, который не очень хорошо сочетается с современными архитектурами процессора. Что само по себе является действительно хорошим основанием для использования uint вместо ushort. Получение тяги из Ushort затруднено, вам нужно много их, прежде чем затраты на их манипулирование - весит экономию памяти. Не только из-за ограниченной спецификации CLI, ядро ​​x86 занимает дополнительный цикл процессора, чтобы загрузить 16-битное значение из-за байта префикса операнда в машинный код. На самом деле не уверен, что это все еще так, но когда-то он возвращался, когда я все еще обращал внимание на циклы подсчета. Собака год назад.


Обратите внимание, что вы можете чувствовать себя лучше об этих уродливых и опасных забросов, позволяя C# компилятор генерировать тот же код, который генерирует компилятор VB.NET. Таким образом, вы получаете исключение OverflowException, когда литье оказалось неразумным. Используйте «Проект»> «Свойства»> вкладка «Строка»> «Дополнительно»> отметьте флажок «Проверить арифметическое переполнение/недополнение». Только для сборки Debug. Почему этот флажок не включен автоматически шаблоном проекта, является еще одним очень мистифицирующим вопросом, решение, которое было сделано слишком давно.

+0

«Это не сложно, просто преобразуйте ushort в uint, конверсия, которая всегда действительна». В самом деле, это то, о чем я думал, но он не будет преобразован в uint, а в int, о котором никто еще не смог объяснить. Очень хороший ответ, хотя! – lesderid

+1

Да, я наложил это, исправлено. Посмотрите на таблицу, на которую я указал, у нее нет неподписанных типов. –

+0

А, я вижу. Думаю, это отвечает на мой вопрос! Я награждаю Habib.OSU щедростью, хотя, поскольку он полностью ответил на исходный вопрос, прежде чем я его отредактировал. – lesderid

15
ushort x = 5, y = 12; 

Следующий оператор присваивания будет производить ошибку компиляции, потому что арифметическое выражение в правой части оператора присваивания имеет значение INT по умолчанию.

ushort z = x + y; // Error: conversion from int to ushort 

http://msdn.microsoft.com/en-us/library/cbf1574z(v=vs.71).aspx

РЕДАКТИРОВАТЬ:
В случае арифметических операций над USHORT, операнды преобразуются к типу, который может содержать все значения. Таким образом, можно избежать переполнения. Операнды могут меняться в порядке int, uint, long и ulong. См. C# Language Specification В этом документе перейдите к разделу 4.1.5 «Интегральные типы» (около страницы 80 в документе слова). Здесь вы найдете:

Для бинарного +, -, *, /,%, &, ^, |, ==, =>, <,> =, и < = операторы, то операнды преобразуются в тип T, где T является первым из int, uint, long и ulong, что может полностью отображать все возможные значенияобоих операндов. Затем выполняется операция с использованием точности типа T12, а тип результата - T (или bool для операторов связи ). Недопустимо, чтобы один операнд имел длину , а другой - типа ulong с бинарными операторами.

Эрик Липпер заявил в question

Арифметика никогда не делается в трусах в C#. Арифметика может быть выполнена в ints, uints, longs и ulongs, но арифметика никогда не делается в шортах. Шорты продвигают к int, и арифметика выполняется в ints, потому что, как я уже говорил, подавляющее большинство арифметических вычислений вписывается в int. Подавляющее большинство не вписывается в короткий. Короткая арифметика возможно медленнее на современном оборудовании, которое оптимизировано для ints, а короткая арифметика не занимает меньше места; это будет сделано в ints или longs на чипе.

+0

Это все еще не объясняет, почему он становится int, а не uint. – lesderid

+1

@lesderid, это связано с тем, что приоритет оператора + перегрузки задан в спецификации C# (7.8.4, стр. 192). В этом первая перегруженная сигнатура - int operator + (int x, int y), а затем ее uint-оператор + (uint x, uint y). Вот почему это становится не uint – Habib

5

С # языка спецификации C:

7.3.6.2 Двоичные числовые рекламные акции Двоичное числовое продвижение происходит для операндов предопределенных +, -, *, /,%, &, |, ^, ==,! =,>, <,> = и < = двоичных операторов. Двоичное числовое продвижение неявно преобразует оба операнда в общий тип, который в случае нереляционных операторов также становится типом результата операции. Двоичное числовое продвижение состоит из применения следующих правил в следующем порядке:

. Если любой из операндов имеет тип десятичный, другой операнд преобразуется в тип десятичного или возникает ошибка привязки, если другой операнд имеет тип float или double.

· В противном случае, если любой из операндов имеет тип double, другой операнд преобразуется в тип double.

· В противном случае, если любой из операндов имеет тип float, другой операнд преобразуется в тип float.

· В противном случае, если любой из операндов имеет тип ulong, другой операнд преобразуется в тип ulong или возникает ошибка привязки, если другой операнд имеет тип sbyte, short, int или long.

· В противном случае, если любой из операндов имеет тип long, другой операнд преобразуется в длинный.

· В противном случае, если любой из операндов имеет тип uint, а другой операнд имеет тип sbyte, short или int, оба операнда преобразуются в тип long.

· В противном случае, если любой из операндов имеет тип uint, другой операнд преобразуется в тип uint.

· В противном случае оба операнда преобразуются в тип int.

+0

Это действительно подтверждает мои выводы, но на самом деле это не объясняет, почему это происходит. – lesderid

+1

@lesderid: Я не понимаю. Это происходит потому, что вы используете компилятор, который соответствует спецификации C#. Возможно, вы задаете аргументы в разделе 7.3.6.2 и почему правила останавливаются на int и не продолжают идти на более мелкие типы? –

+1

Ну да, меня интересует, почему спецификация говорит, что оба операнда должны быть преобразованы в int. Я просто добавил ошибки компилятора, чтобы уточнить, и потому, что я не был уверен, было ли это спецификацией или решением для реализации. – lesderid

3

Нет причин, которые предназначены. Это просто эффект или применение правил разрешения перегрузки, которые утверждают, что первая перегрузка, для параметров которой есть неявное преобразование, соответствующее аргументам, будет использоваться перегрузка.

Это указано в спецификации C#, раздел 7.3.6 следующим образом:

Числовой продвижение не является отдельным механизмом, а скорее эффект применения разрешения перегрузки для предопределенных операторов.

Он идет по иллюстрированию с примером:

В качестве примера цифровой рекламы, рассмотрим предопределенные реализации двоичного оператора *:

INT оператор * (Int х, внутр у);

uint operator * (uint x, uint y);

длинный оператор * (длинный х, длинный y);

ulong operator * (ulong x, ulong y);

float operator * (float x, float y);

двойной оператор * (двойной x, двойной y);

десятичный оператор * (десятичный x, десятичный y);

Когда к этому набору операторов применяются правила разрешения перегрузки (§7.5.3), эффект заключается в том, чтобы выбрать первый из операторов, для которых неявные преобразования существуют из типов операндов. Например, для операции b * s, где b является байтом, а s является коротким, разрешение перегрузки выбирает оператор * (int, int) как лучший оператор.

2

Ваш вопрос на самом деле немного сложный. Причина, по которой эта спецификация является частью языка, - это ... потому что они приняли это решение, когда создали язык. Я знаю, что это звучит как разочаровывающий ответ, но так оно и есть.


Однако реальный ответ, вероятно, будет включать в себя множество контекстных решений в тот же день в 1999-2000 годах. Я уверен, что команда, которая сделала C#, имела довольно решительные дебаты по всем этим языковым данным.

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

The quote above is from Wikipedia C#

Все эти цели дизайна, возможно, повлияли на их решение. Например, в 2000 году большая часть системы была уже родной 32-битной, поэтому они, возможно, решили ограничить число переменных меньше, чем это, так как при выполнении арифметических операций она будет преобразована в любом случае по 32 бита. Это, как правило, медленнее.

В этот момент вы можете спросить меня; если есть неявное преобразование на те типы, почему они включали их в любом случае? Ну, одна из их целей дизайна, как указано выше, является переносимостью.

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

Это решение, которое Java не сделал. Например, если вы пишете Java-программу, которая взаимодействует с программой на C++, то в результате вы получаете значения ushort, а у Java только короткие (которые подписаны), поэтому вы не можете легко назначить друг другу и ожидать правильные значения.

Я позволю вам сделать ставку, следующий доступный тип, который может получить такое значение в Java, является int (32-бит, конечно). Вы только что удвоили свою память здесь. Что не может быть большой проблемой, вместо этого вам нужно создать экземпляр массива из 100 000 элементов.

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

Но теперь я чувствую, что я расхохотался из первоначального вопроса.


Так что ваш вопрос является хорошим и, надеюсь, я был в состоянии принести некоторые ответы на вас, несмотря на то, если я знаю, что это, вероятно, не то, что вы хотели услышать.

Если вы хотите, вы могли бы даже больше узнать о спецификации C#, ссылки ниже. Есть интересная документация, которая может быть интересна для вас.

Integral types

The checked and unchecked operators

Implicit Numeric Conversions Table

Кстати, я полагаю, вы, вероятно, следует наградить habib-osu за него, так как он дал достаточно хороший ответ на первоначальный вопрос с правильной ссылки. :)

С уважением

Смежные вопросы