开发者

Don’t Checked and Unchecked establish an overflow context only during runtime?

int int1;
byte byte1;

unchecked
{
    int1 = 2147483647 + 10; //ok
    byte1 = 200+100; //error
}

1)Only difference between int1 and byte1 overflows is that the latter happened during narrowing conversion attempt, but the end result ( overflowing ) is the same, so why doesn't compiler also ig开发者_JAVA技巧nore byte1 overflow ( I'm aware that compiler allows constant expressions of type int to be converted to smaller type only if the value is within the range of the target type, but I would expect compiler to ignore such overflows if they happened inside unchecked context)?

2) Aren’t checked and unchecked operators/statements only “active” during runtime (ie don’t they establish overflow checking context only during runtime)? If so, then in above code compiler should also report an error when int1 overflowed, since constant expressions are inspected during compile time, and at that point unchecked statement is not yet “active” and thus it won’t yet establish unchecked overflow context?1

thank you


1) The constant expression for your byte is of type int, with an implicit conversion as specified in section 6.1.9 of the C# 4 spec:

A constant-expression (§7.19) of type int can be converted to type sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant-expression is within the range of the destination type.

Clearly the constant expression isn't within the range of the destination type here.

For the integer value, I believe it's not performing promotion to long and then trying to perform a narrowing conversion to int - it's performing the arithmetic with int overflow, as per section 7.19:

The compile-time evaluation of constant expressions uses the same rules as run-time evaluation of non-constant expressions, except that where run-time evaluation would have thrown an exception, compile-time evaluation causes a compile-time error to occur.

In other words, the constant expression is using +(int, int) as both operands are integers... that's not complaining in an unchecked context, because the overflow wouldn't happen at execution time either.

2) No, checked and unchecked make a difference at compile time too. From section 7.19:

Unless a constant expression is explicitly placed in an unchecked context, overflows that occur in integral-type arithmetic operations and conversions during the compile-time evaluation of the expression always cause compile-time errors (§7.19).


(For the sake of interest, I'd like to point out that the Microsoft C# compiler doesn't quite comply with the rules of the middle quoted paragraph anyway... there are some decimal operations where the results of performing the arithmetic at compile-time are different than the same operations performed at execution time, IIRC. Just a bit of trivia. It'll be interesting to see whether the compiler-as-a-service version (i.e. managed code) behaves the same way here.)


It's best not to think of the unchecked or checked contexts (whether from compilation settings or the unchecked and checked keywords) as affecting whether or not something errors. Rather it's better to think of it as changing the meaning.

In an checked context, x + y (where x and y are both ints) means "take the integral number x (in the range [-2147483648–+2147483647]) and the number y (in the range [-2147483648–+2147483647]), add them, and return the number (in the range [-2147483648–+2147483647]) obtained by that addition".

In other words, it means much the same thing as we explain to young children when we're teaching them arithmetic, but with some range limitations applied, mostly as an implementation detail. If the code can't do what we ask, because it can't keep to that range limitation, then we get an exception. Just like we get an exception when we try to read a file that doesn't exist or anything else that isn't actually possible.

In an unchecked context x + y means "treat x and y as a pair of 32-bit twos-complement binary numbers, and perform binary addition on them, ignoring any overflow on the final bit, and return the number).

Now, if you have a two-complement machine, then the only practical difference between this and the most natural implementation of the checked form is that we don't get an exception in the case of overflow.

However, there's both a technical and conceptual difference difference beyond this.

The technical difference is that if a machine where not twos-complement, it would have to fake it to provide the required behaviour (in particular the range of int is not that of a 32-bit ones-complement integer).

The conceptual difference is more important. When you are doing unchecked addition, then for 2000000000 + 2000000000 the result of -294967296 is the right answer. It's not a failure condition, like the exception we get if we do the same addition checked, it's not a broken attempt to store 4000000000 in a 32-bit twos complement binary number, it is the precisely correct result we want.

We don't want this is we have two boxes full of 2000000000 marbles each and try to add up all the marbles. We do want this when addition is used in certain bit-twiddling operations or where losing information is part of the intent (e.g. mult-and-add hashing methods).

The difference between checked and unchecked is not about safety vs speed, but about having two different meanings, albeit meanings that overlap at the ranges where overflow doesn't happen.

Now, considering all of that, let's consider what the following mean:

byte b0 = 100 + 100;
byte b1 = (byte)(100 + 100);

The first means, "take 100, add 100 to it, and put the result into the byte b0, which it will happily fit into". The second means "take 100, add 100 to it, and then force the result into b1 whether it wants to fit or not".

Being checked or unchecked can't change the way the first works. Whatever way addition is worked out, either we can state "it will happily fit into the variable" or we cannot.

Being checked or unchecked those though affect the latter, in changing how overflow is treated in the forcible fitting entailed by the explicit conversion. Hence doubling the values to:

byte b0 = 200 + 200;
int i = 200;
byte b1 = (byte)(i + 200);

Will have the following results:

  1. The first line won't compile whatever the checking context is, because "will happily fit into the variable" isn't true in either case.
  2. The second line will compile in either case (if we didn't use i as a part-way then it wouldn't in a checked context, but would in an unchecked).
  3. The second line will result in overflow exception in a checked context.
  4. The second line will result in b1 being given the value 144 in an unchecked context.

In particular, note the difference between byte b = 100 + 100; which assigns a literal byte and byte b = x + y', which is not allowed as even if x and y are bytes, the result of their addition is int. There is no overflow involved here, but it does show the other side of why your byte assignment isn't allowed.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜