Integer summing blues, short += short problem
C语言程序:
1 2 3 4 5 6 7 8 9 10
| 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); |
- 第一个失败是因为在规范中定义的short+short=int,正如byte+byte=int,但是我希望第二个也会失败,所以我期待看到这里的推理。
- 你找到原因了吗?
- 埃里克·利珀特在下面尽可能详细地回答了这个问题,所以我不确定你在这里问什么。简而言之,是因为a += b与a = (short)(a+b)相同。
- 字节+字节=整数可能重复…为什么?
这里有两个问题。第一个问题是"为什么短加短的结果是int?"
好吧,假设short加short是short,看看会发生什么:
1 2
| short[] prices = { 10000, 15000, 11000 };
short average = (prices[0] + prices[1] + prices[2]) / 3; |
当然,如果计算是在短时间内完成的话,平均值是-9845。和大于最大可能的短,所以它绕到负数,然后除以负数。
在一个整数算术包围的世界里,用int来做所有的计算更为明智,int是一种类型,它可能有足够的范围使典型的计算不会溢出。
第二个问题是:
- short加short为int
- 将int赋给short是非法的
- A+=B与A=A+B相同
- 因此short+=short应该是非法的
- 为什么这是合法的?
这个问题的前提不正确;上面第三行是错误的。C规范在第7.17.2节中规定
Otherwise, if the selected operator is
a predefined operator, if the return
type of the selected operator is
explicitly convertible to the type of
x, and if y is implicitly convertible
to the type of x or the operator is a
shift operator, then the operation is
evaluated as x = (T)(x op y), where T
is the type of x, except that x is
evaluated only once.
编译器将代表您插入强制转换。正确的推理是:
- short加short为int
- 将int赋给short是非法的
- s1+=s2与s1=(短)(s1+s2)相同
- 因此这应该是合法的
如果它没有为您插入强制转换,那么就不可能在许多类型上使用复合赋值。
- 你的第一个样品不好。如果你用int替换short,一切都会好起来的,但是如果我们用更大的数字(使和大于2^31),它会导致int溢出。我的问题是为什么short + short = int(对于byte)和int + int = int(例如,时间不长)。
- 为什么它决定了int不应该扩大到long?我可以理解扩展字节和短路计算的原因,但似乎int上的算术也可能溢出。这仅仅是对32位体系结构中存在维护32位寄存器结果的方便指令这一事实的一个让步吗?或者这里有更深层次的东西吗?
- @卢布什金:真的"同样可能"吗?我以编写编译器为生,每天都要编写超过2^15种类型的程序。如果将一组不同程序集中的类型数相加,很容易溢出一小段。在编译器空间中,我从来没有遇到过这样的问题,在那里我可以合理地溢出一个int。在编译器成功执行涉及20亿类型的程序的算术之前,它会耗尽虚拟内存。在大多数情况下,32位溢出是不可能的。
- @安德烈:是的,整数算术可能溢出。如果您想避免这种情况,那么可以使用checked运算符来确保整数溢出会导致异常。如果你觉得你将要处理的整数足够大,以至于它们上面的普通数学运算符可能溢出,那么你首先应该用long来做你所有的算术。
- @埃里克·利珀特,我只是想知道为什么short和int的行为不同。
- @eric:int用于许多计算环境,其中一些与内存/进程空间没有关系。随着计算硬件的功率和容量的增加,这种界限将很快被定期超越。但也许表达我观点的更好方法是,如果编译器在执行算术时决定将较小的类型扩展到较宽的类型,以避免意外的溢出,那么它应该对所有具有可用的较宽类型的类型进行扩展,以保持范围和表示准确性。选择在int停止(而不是扩展到long)有点出乎意料。
- @安德烈:因为短裤和丁字裤很不一样。在整数中进行的大多数实际计算只使用int范围的一小部分;这些相同的计算使用短的全部或更多范围。大多数现代处理器都是以32位本地执行整数运算;您必须回到原来的时间,才能找到以16位本地执行整数运算的处理器。我上一次在16位Windows编译器上工作是在1997年。
- @卢布什金:好的,那么你会让我们(1)说int+int是长的吗?这样做就等于说所有的计算都是长期进行的。您不想让人们在每个整数计算中插入强制转换。(2)C是否有不同的行为取决于架构,就像C一样?(3)像vbscript一样,自动扩展为更大的类型;放弃在c中的静态类型。(4)还有别的吗?每种方法都有严重的性能或可移植性成本。成本值吗?
- @埃里克:所有这些都是很好的观点。到1,C是一种语言,所有的算术都是在INTS中执行的。除了性能方面的影响,我看不出有什么理由更喜欢窄的类型而不是宽的类型。然而,性能很可能是决定因素。为了2,我同意不同架构上的不同行为是不可取的,应该避免。我认为我们也不想放弃静态类型。顺便说一句,别误会我;我只是想了解设计决策背后的想法,这些想法将用于创建C语言。
- @埃里克:我想我会争辩说,有时对类型加宽发生的时间进行更细粒度的控制会更好。我经常想通过升级到int(因为我基本上是在执行逐位计算)来对不希望发生的字节或短字节执行计算。允许这样做的语法(可能类似于选中/未选中的块)。
- @你为什么要避免这种晋升?我向您保证,clr运行时层和最终的cpu都将把它们扩展到int。假设为了讨论的目的,我们希望使用short+short-stay-in-short。IL层没有该操作的操作码。它只有整数加法的操作码。不管怎样,CLR都会把那些短裤换成整数。假设我们在IL中添加了简短的加法操作符,那么抖动会做什么呢?当芯片注册时,它将把操作数扩展到32位。
- @埃里克:通常情况下,字节和空号是用来表示数字的,所以你希望它们的行为符合数字的语义。但有时你只需要一袋有序的比特-你想用算术运算来计算它们的副作用。^、、&;、<<和>>位运算符保留了它们应用于的表达式的类型-但有时它也可以用于if+、-、*和/这样做。带环绕的算术有时很有用。但是,我理解您的观点,即它是否对足够多的人设计、开发和测试C足够有价值。
- @埃里克:另外,我觉得至少在x86体系结构中,可以只在16位的非扩展寄存器上执行移动、加载和算术。您还可以访问低和高8位子寄存器(例如,AX中的al和ah)。显然,非x86体系结构上的行为可能存在实质性的差异。
- 很好的回答…
好吧,+=操作符说你会用一个短的时间增加a的值,而=操作符说你会用一个操作的结果覆盖这个值。操作a + b生成一个int,不知道它可以做其他操作,而您正试图将该int分配给一个short。
- 那么增值和增值之间有什么区别呢?
- 我真的没想到。我以为+=只是个速记!
- @jak我把它理解为=操作符分配了一个新的值(在这种情况下是错误的类型),但是+=使用了不同的实现(即,它按我们期望的那样添加)。
- 我们也知道A+=B和A=A+B一样…
- 我也一直认为a+=b只是a=a+b的简写,所以我真的希望看到这里的确认能被完全说服。
- @Ø;yvind br&229;那么它似乎是这样的:a+=b=a=a+b,除非+=操作符过载,并且它似乎是针对short类型,并且没有+对于short,shortA + shortB变宽,变成(int)shortA + (int)shortB。
- @卡米洛——看起来是这样,但我还是想确认一下;)
- @卡米洛:没有"重载+=运算符"这样的事情。复合运算符不能在C中重载。
你必须使用:
至于分配行为和附加分配行为之间的区别,我认为这与此有关(来自msdn)
1 2 3 4 5 6
| 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 ). |
然而,它有点含糊,所以马鞭有一个更深的理解可以评论。
- A=(短)A+B//这也不管用,亲爱的,,,显示同样的问题……
- @杰克:你试过用两组括号吗?您的注释中的代码不同。
- 在msdn上怎么说的?如果+是一个预先定义的运算符,这是错误的,在本例中,显然是这样。在这种情况下,它等于规定的x=(t)(x+y)。如果你能给我发送这个页面的链接,我会提醒文档管理员。
- @埃里克:当然,在这个页面上:msdn.microsoft.com/en-us/library/sa7629ew.aspx
这是因为int是定义+的最小有符号类型。任何较小的值都首先被提升为int。+=运算符是针对+定义的,但具有处理不符合目标的结果的特殊情况规则。
- 这个答案不正确。+=没有为每个数值类型定义。见C规范第7.17.2节。
这是因为+=是作为重载函数实现的(其中一个是short,编译器选择最具体的重载)。对于表达式(a+b),编译器在赋值之前默认地将结果扩展到int。
- 重载运算符只能用类、结构等用户定义的运算符实现。但它是一个内置的+算术运算符。不能直接重载+=运算符,但用户定义的类型可以重载+运算符。有关详细信息,请访问以下链接:msdn.microsoft.com/en-us/library/sa7629ew(v=vs.71).asp&zwnj;&8203;x
- 杰克是对的。+=运算符不是C中的重载函数。