2012-03-04 35 views
9

Tôi không thể hiểu tại sao mã bên dưới biên dịch.Tại sao trình biên dịch C# không phàn nàn với Tràn cho quá trình đúc 'xấu' rõ ràng này?

public void Overflow() 
{ 
    Int16 s = 32767; 
    s = (Int16) (s + 1); 
} 

Tại thời điểm biên dịch, rõ ràng là (s +1) không phải là Int16 nữa khi chúng tôi biết giá trị của s.

Và CLR cho phép đúc để:

  • Để loại riêng của mình
  • Hoặc bất kỳ của các cơ sở-loại (vì nó là an toàn)

Như Int32 là không Int16 và Int16 là không phải loại cơ sở của Int32.

Câu hỏi: Vậy tại sao trình biên dịch không thành công cho quá trình truyền ở trên? Bạn có thể vui lòng giải thích nó từ CLR và trình biên dịch quan điểm?

Cảm ơn

+1

Bạn sử dụng dàn diễn viên để * chặn * tràn. Nếu bạn muốn một thì sử dụng Convert.ToInt16() để thay thế. –

Trả lời

12

Kiểu của biểu thức s + 1Int32 - cả hai toán hạng được chuyển đổi thành Int32 trước việc bổ sung được thực hiện. Vì vậy, mã của bạn tương đương với:

public void Overflow() 
{ 
    Int16 s = 32767; 
    s = (Int16) ((Int32) s + (Int32) 1); 
} 

Vì vậy, sự tràn chỉ thực sự xảy ra trong dàn diễn viên rõ ràng.

Hoặc, để đặt theo cách khác: vì đặc tả ngôn ngữ nói như vậy. Bạn nên mô tả một trong số:

  • Tại sao bạn nghĩ rằng trình biên dịch được vi phạm các đặc điểm kỹ thuật ngôn ngữ
  • Sự thay đổi chính xác mà bạn đang đề xuất với đặc điểm kỹ thuật ngôn ngữ

EDIT: Chỉ cần để làm cho mọi việc thực sự rõ ràng (dựa trên ý kiến ​​của bạn), trình biên dịch sẽ không cho phép điều này:

s = s + 1; 

khi s là một Int16bất cứ điều gì giá trị của s có thể được biết đến. Không có hành Int16 operator+ (Int16, Int16) - như thể hiện trong phần 7.8.4 của C# 4 spec, các nhà khai thác nguyên bổ sung là:

int operator +(int x, int y); 
uint operator +(uint x, uint y); 
long operator +(long x, long y); 
ulong operator +(ulong x, ulong y); 
+0

Có.Và đặc tả ngôn ngữ không nói rằng trình biên dịch được cho là phân tích lưu lượng dữ liệu và "biết" rằng s sẽ có giá trị không đổi (trừ khi bạn thực sự khai báo nó là const, mà bạn không làm). – usr

+1

Jus dưới dạng ghi chú - S + 1 là int32 vì "1" là int32. – TomTom

+0

@usr: Nếu nó không phân tích thì tại sao "s = s + 1;" không biên dịch? – pencilCake

1

"Vào thời điểm biên dịch rõ ràng là (s + 1) không phải là một Int16 nữa như chúng ta biết giá trị của s. "

Chúng tôi biết giá trị của s + 1 quá ngắn; trình biên dịch không. Trình biên dịch biết ba điều:

  1. Bạn có một biến ngắn 's'
  2. Bạn gán nó một hằng số tắt hợp lệ.
  3. Bạn đã thực hiện một hoạt động không tách rời giữa hai giá trị có chuyển đổi ẩn thành int.

Có, trong trường hợp cụ thể này nó là tầm thường để xác định rằng kết quả là quá lớn để phù hợp với một đoạn ngắn khi bạn định kiểu trở lại, nhưng để xác định rằng trình biên dịch sẽ được yêu cầu để thực hiện số học tại thời gian biên dịch , sau đó thực hiện kiểm tra xác thực typecast trên kết quả. Với các ngoại lệ rất hiếm (tất cả được gọi rõ ràng trong spec, hầu hết liên quan đến hằng số không có giá trị) trình biên dịch không kiểm tra các kết quả của hoạt động, chỉ có các loại hoạt động và các loại hoạt động của bạn là chính xác.

Ngoài ra, danh sách các trường hợp mà trình biên dịch cho phép truyền là hoàn toàn không đủ. Trình biên dịch cho phép các kiểu gõ xuất hiện trong một loạt các tình huống khá rộng, nhiều trong số chúng hoàn toàn vô hình đối với CLR. Ví dụ: có các chuyển đổi loại tiềm ẩn và rõ ràng được tích hợp vào ngôn ngữ cho mọi kiểu số gần như mọi loại số khác. nơi tốt để tìm thêm thông tin về các quy tắc định kiểu:

+0

Tò mò rằng 'long' được chuyển đổi hoàn toàn thành' float', nhưng 'double' không, mặc dù thực tế là đối với mọi giá trị' double' có thể, có chính xác một giá trị 'float '' float' và 'long' 'có thể mất rất nhiều độ chính xác trong chuyển đổi. Trên thực tế, tôi muốn đặt ra rằng các chuyển đổi từ 'float' thành' double' có nhiều khả năng bị lỗi hơn 'double' thành' float'. Ví dụ, 'float f = (float) (1.0/10.0);' là đúng trong một nửa bit có ý nghĩa ít nhất, trong khi 'double d = 1.0f/10.0f;' bị tắt bởi hàng triệu. – supercat

1

Nói chung rằng dàn diễn viên nói: "Tôi đang làm điều này cố ý, không phàn nàn", do đó cho trình biên dịch để khiếu nại sẽ là hành vi đáng ngạc nhiên.

Thực tế không có tràn vì quảng bá ngầm của các đối số. Tuy nhiên, dàn diễn viên cắt ngắn kết quả 32 bit sao cho kết quả không bằng nhau theo số học s + 1; nhưng bởi vì bạn yêu cầu một cách rõ ràng các diễn viên, trình biên dịch sẽ không phàn nàn - nó đang làm chính xác như bạn đã yêu cầu.

Ngoài ra, có nhiều trường hợp khi tràn "xung quanh" (hoặc modulo-2 n số học) là cố ý và bắt buộc. Trình biên dịch sẽ giả định hợp lý rằng nó là cần thiết nếu bạn rõ ràng cast vào một kiểu nhỏ hơn. Nó là xuống để các lập trình viên để chọn một loại thích hợp cho hoạt động, và nếu tràn là không mong muốn một float, double hoặc decimal có thể là loại số học thích hợp hơn so với các loại số nguyên hệ thống hạn chế.

+0

mà không cần truyền mã sẽ không biên dịch, nhưng nó không liên quan gì đến phạm vi và mọi thứ cần làm với loại không phù hợp. –

+0

@Michael: Cảm ơn bạn đã làm rõ (lập trình C++, người dùng C# không thường xuyên!). Tôi có nghĩa là "yêu cầu một cách rõ ràng các diễn viên" như trái ngược với "thay đổi loại biến", và đã không cho thấy rằng các diễn viên một mình được bỏ qua. Vấn đề là nếu trình biên dịch nói với bạn rằng có một kiểu không phù hợp và bạn "giải quyết" nó bằng cách chuyển sang kiểu không thích hợp, thay vì thay đổi kiểu biến được gán, đó không phải là "có thể phát hiện" được bởi trình biên dịch vì nó có thể là lỗi ngữ nghĩa, hoặc nó có thể là một yêu cầu ứng dụng. – Clifford

-2

Cảm ơn tất cả các bạn đã giải thích.

Hãy để tôi cũng thêm một trích dẫn từ cuốn sách Jeffry Richter của điều này giải thích tại sao biên dịch không thất bại khi bạn cố gắng để cast Int16 để Int32 trong khi họ đang không có nguồn gốc từ nhau:

Từ trang 116:

"(...) Trình biên dịch C# có kiến ​​thức thân mật về các kiểu nguyên thủy và áp dụng các quy tắc đặc biệt của riêng nó khi biên dịch mã số . công việc viết mã như exp ected. "

+4

Không có hành vi phạm tội với Jeffry Richter, nhưng đó là một lời giải thích khá lame. Bạn đã nhận được một số câu trả lời tốt hơn nhiều rồi. –

Các vấn đề liên quan