整数加总布鲁斯,短+ =短问题


72

C#中的程序:

short a, b;
a = 10;
b = 10;
a = a + b; // Error : Cannot implicitly convert type 'int' to 'short'.

// we can also write this code by using Arithmetic Assignment Operator as given below

a += b; // But this is running successfully, why?

Console.Write(a);

5
第一个失败,因为short + short = int符合规范中的定义,就像byte + byte = int一样,但是我希望第二个也失败,所以我很期待在这里看到推理。
岛之风Bråthen如此阐述

你发现什么原因了吗?
Mohammad Jahangeer Ansari

以下是埃里克·利珀特(Eric Lippert)尽可能详细地回答的内容,因此,我不确定您在这里提出什么要求。总之是,因为a += b是相同的a = (short)(a+b)
的ØyvindBråthen如此阐述道

Answers:


77

这里有两个问题。第一个是“为什么short加short结果为int?”

好吧,假设short加short是short,然后看看会发生什么:

short[] prices = { 10000, 15000, 11000 };
short average = (prices[0] + prices[1] + prices[2]) / 3;

如果此计算以简而言之,则平均值当然为-9845。总和大于可能的最大空头,因此将其环绕为负数,然后将负数相除。

在整数运算环绕的世界中,以int进行所有计算更为明智,这种类型很可能具有足够的范围以使典型计算不会溢出。

第二个问题是:

  • 短加短是整数
  • 将int赋给short是非法的
  • a + = b与a = a + b相同
  • 因此short + = short应该是非法的
  • 那为什么合法呢?

这个问题的前提不正确;上面的第三行是错误的。C#规范在7.17.2节中规定

否则,如果选择的运算符是预定义的运算符,则如果选择的运算符的返回类型可以显式转换为x的类型,并且如果y隐式转换为x的类型,或者该运算符是shift运算符,则该操作被评估为x =(T)(x op y),其中T是x的类型,只是x仅被评估一次。

编译器代表您插入演员表。正确的推理是:

  • 短加短是整数
  • 将int赋给short是非法的
  • s1 + = s2与s1 =(short)(s1 + s2)相同
  • 因此这应该合法

如果它没有为您插入演员表,那么就不可能在许多类型上使用复合赋值。


11
您的第一个样本不好。如果将其替换shortint所有内容,则一切正常,但如果我们放置较大的数字(使总和大于2 ^ 31),则将导致int溢出。我的问题是为什么short + short = int(and for byte)和int + int = int(例如不久)。
安德烈(Andrey)2010年

8
@LBushkin:真的“可能”吗?我以谋生为目的来编写编译器,并且每天都要处理具有超过2 ^ 15种类型的程序。如果我将一堆不同的程序集中的类型数量加在一起,我很容易会出现一个简短的溢出。我从来没有在编译器空间遇到过问题,因为我可以合理地溢出一个int。编译器在进行算术运算之前,将耗尽虚拟内存,而算术涉及其中的二十亿个类型。在大多数情况下,它根本不可能具有32位溢出。
埃里克·利珀特

6
@Eric:int用于许多计算上下文中,其中一些与内存/进程空间无关。随着计算硬件功能和容量的增加,这种边界将很快很快被突破。但是表达我的观点的一种更好的方法可能是,如果编译器在执行算术时决定将较小的类型扩展为较宽的类型,以避免意外的溢出,则应该对所有具有可用的较宽类型的类型进行保留,以保持范围和表示精度。停止在int(而不是扩展到long)的选择有点出乎意料。
LBushkin

5
@LBushkin:好的,所以您可以让我们(1)说int​​ + int很长吗?这样做无异于说所有的计算总是在很长时间内完成的。您不想让人们在每个整数计算中插入强制转换。(2)C会像C一样,根据体系结构具有不同的行为吗?(3)像VBScript一样自动扩大到更大的类型;放弃使用C#进行静态输入。(4)还有什么?这些方法中的每一种都有严重的性能或可移植性成本。值得吗?
埃里克·利珀特

4
@Eric:另外,我的印象是,至少在x86架构上,可以仅对16位非扩展寄存器执行移动,加载和算术运算。您还可以访问低8位和高8位子寄存器(例如AX中的AL和AH)。显然,在非x86架构上的行为可能存在本质上的差异。
LBushkin

13

好吧,+=操作员说您将a用short增加值,而=说您将用操作结果覆盖该值。该操作会a + b产生一个int,不知道它可以做其他事情,而您正在尝试将该int分配给short。


1
那么增加值和增加值有什么区别?
Mohammad Jahangeer Ansari

我真的没想到这一点。我以为+=只是速记!
卡米洛·马丁

@jak我理解它,因为=运算符分配了一个新值(在这种情况下,类型错误),但是+=使用了不同的实现(即,它按我们期望的那样添加了)。
卡米洛·马丁

我也一直认为a + = b只是a = a + b的简写,所以我真的很想在这里看到一个可以完全说服的确认。
岛之风Bråthen如此阐述

@岛之风Bråthen如此阐述它似乎是这样的:a+=b=a=a+b除非+ =运算符重载,似乎它是类型short,而且也没有+shortshortA + shortB扩大并成为(int)shortA + (int)shortB
卡米洛·马丁

9

您必须使用:

a = (short)(a + b);

关于赋值和加法赋值行为之间的区别,我想这与它有关(来自msdn)

x+=y
is equivalent to
x = x + y
except that x is only evaluated once. The meaning of the + operator is
dependent on the types of x and y (addition for numeric operands, 
concatenation for string operands, and so forth).

但是,这有点含糊,因此,具有较深了解的人可以发表评论。


1
@jak:您是否尝试过两套括号?您注释中的代码是不同的。
2010年

2
在MSDN上怎么说呢?如果+是预定义的运算符,那是错误,在这种情况下,显然是。在这种情况下,按指定值等效于x =(T)(x + y)。如果您可以将页面的链接发送给我,请注意文档管理器。
埃里克·利珀特


7

发生这种情况是因为int是为其+定义的最小带符号类型。任何较小的东西都首先提升为int。该+=运营商相对于定义的+,但处理不符合目标结果的特殊情况规则。


3
这个答案是不正确的。没有为每种数字类型定义+ =。请参阅C#规范的7.17.2节。
埃里克·利珀特

1

这是因为+ =是作为重载函数实现的(其中一个是短函数,编译器会选择最具体的重载)。对于表达式(a + b),编译器在赋值之前默认将结果扩展为int。


1
重载运算符只能使用用户定义的运算符(如class,structure)来实现。但是它是一个内置的算术运算符。+ =运算符不能直接重载,但是用户定义的类型可以重载+运算符,有关详细信息,请访问链接,msdn.microsoft.com /
Mohammad Jahangeer Ansari 2010年

1
贾克是正确的。+ =运算符不是C#中的重载函数。
埃里克·利珀特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.