2012-04-08 35 views
19

Trước đây hôm nay tôi đã cố gắng thêm hai ushorts và tôi nhận thấy rằng tôi đã phải đưa kết quả trở lại ushort. Tôi nghĩ rằng nó có thể đã trở thành một uint (để ngăn chặn một tràn không lường trước có thể xảy ra?), Nhưng tôi ngạc nhiên nó là một int (System.Int32).Tại sao ushort + ushort bằng int?

Có một số lý do thông minh cho điều này hoặc có thể vì int được xem là loại số nguyên 'cơ bản'?

Ví dụ:

Chỉnh sửa: Giống như GregS' câu trả lời cho biết, C# spec nói rằng cả hai toán hạng (trong ví dụ này 'a' và 'b') nên được chuyển đổi sang int. Tôi quan tâm đến lý do cơ bản cho lý do tại sao đây là một phần của spec: tại sao không C# spec cho phép hoạt động trực tiếp trên các giá trị ushort?

+3

http://bytes.com/topic/c-sharp/answers/256857-does-ushort-ushort-return-int-value (xem câu trả lời J.Skeets) –

+0

Tôi đã cập nhật câu trả lời của mình từ thông số kỹ thuật C# từ microsoft – Habib

Trả lời

42

Câu trả lời đơn giản và chính xác là "vì đặc tả ngôn ngữ C# nói như vậy".

Rõ ràng bạn không hài lòng với câu trả lời đó và muốn biết "tại sao nó lại nói như vậy". Bạn đang tìm kiếm "nguồn đáng tin cậy và/hoặc chính thức", điều đó sẽ hơi khó khăn. Những quyết định thiết kế này đã được thực hiện từ lâu, 13 năm là rất nhiều con chó sống trong kỹ nghệ phần mềm. Chúng được tạo ra bởi những "bộ đếm thời gian cũ" như Eric Lippert gọi chúng, chúng đã chuyển sang những thứ lớn hơn và tốt hơn và không đăng câu trả lời ở đây để cung cấp một nguồn chính thức.

Tuy nhiên, có thể suy ra, có nguy cơ chỉ đáng tin cậy. Bất kỳ trình biên dịch được quản lý nào, như C#, có ràng buộc rằng nó cần tạo mã cho máy ảo .NET. Các quy tắc được cẩn thận (và khá dễ đọc) được mô tả trong thông số CLI. Đây là thông số kỹ thuật Ecma-335, bạn có thể tải xuống miễn phí from here.

Chuyển sang phân vùng III, chương 3.1 và 3.2. Chúng mô tả hai hướng dẫn IL có sẵn để thực hiện thêm, addadd.ovf. Nhấp vào liên kết đến Bảng 2, "Hoạt động số nhị phân", nó mô tả toán hạng nào được phép cho các lệnh IL đó. Lưu ý rằng chỉ có một vài loại được liệt kê ở đó. byte và ngắn cũng như tất cả các loại unsigned bị thiếu. Chỉ int, dài, IntPtr và dấu phẩy động (float và double) được cho phép. Với các ràng buộc bổ sung được đánh dấu bằng một x, bạn không thể thêm một int vào một ví dụ dài.Những ràng buộc này không hoàn toàn giả tạo, chúng dựa trên những thứ bạn có thể làm hiệu quả hợp lý trên phần cứng có sẵn.

Bất kỳ trình biên dịch được quản lý nào cũng phải giải quyết vấn đề này để tạo ra IL hợp lệ. Điều đó không khó, chỉ cần chuyển đổi ushort thành một loại giá trị lớn hơn trong bảng, một chuyển đổi luôn hợp lệ. Trình biên dịch C# chọn int, loại lớn hơn tiếp theo xuất hiện trong bảng. Hoặc nói chung, chuyển đổi bất kỳ toán hạng nào sang loại giá trị lớn nhất tiếp theo để cả hai đều có cùng loại và đáp ứng các ràng buộc trong bảng.

Tuy nhiên, có một vấn đề mới, một vấn đề khiến các lập trình viên C# khá hấp dẫn. Kết quả của việc bổ sung là loại được quảng cáo. Trong trường hợp của bạn sẽ là int. Vì vậy, thêm hai giá trị ushort của, nói, 0x9000 và 0x9000 có một kết quả int hoàn toàn hợp lệ: 0x12000. Vấn đề là: đó là một giá trị không phù hợp với một nhóm. Giá trị bị tràn. Nhưng nó đã không tràn trong tính toán IL, nó chỉ tràn khi trình biên dịch cố gắng nhồi nhét nó trở lại vào một ushort. 0x12000 bị cắt ngắn thành 0x2000. Một giá trị khác nhau đáng lo ngại chỉ có ý nghĩa khi bạn đếm bằng 2 hoặc 16 ngón tay, không phải với 10.

Đáng chú ý là lệnh add.ovf không giải quyết được vấn đề này. Đây là hướng dẫn sử dụng để tự động tạo ra một ngoại lệ tràn. Nhưng nó không, tính toán thực tế trên ints chuyển đổi đã không tràn.

Đây là nơi đưa ra quyết định thiết kế thực tế. Các bộ định thời cũ dường như đã quyết định rằng chỉ cần cắt ngắn kết quả int để ushort là một nhà máy lỗi. Nó chắc chắn là. Họ quyết định rằng bạn phải thừa nhận rằng bạn biết rằng việc bổ sung có thể tràn và rằng điều đó sẽ ổn nếu điều đó xảy ra. Họ đã làm cho nó vấn đề của bạn, chủ yếu là vì họ không biết làm thế nào để làm cho nó của họ và vẫn tạo ra mã hiệu quả. Bạn phải bỏ. Vâng, điều đó thật đáng buồn, tôi chắc rằng bạn cũng không muốn vấn đề đó.

Khá đáng chú ý là các nhà thiết kế VB.NET đã đưa ra giải pháp khác cho vấn đề. Họ thực sự đã làm cho nó vấn đề của họ và không vượt qua buck. Bạn có thể thêm hai UShort và gán nó cho một UShort mà không có một diễn viên. Sự khác biệt là trình biên dịch VB.NET thực sự tạo ra thêm IL để kiểm tra tình trạng tràn. Đó không phải là mã rẻ, làm cho mỗi lần bổ sung ngắn khoảng 3 lần chậm. Nhưng nếu không, lý do giải thích tại sao Microsoft duy trì hai ngôn ngữ có khả năng tương tự khác.

Ngắn câu chuyện: bạn đang trả giá vì bạn sử dụng loại không phù hợp với kiến ​​trúc cpu hiện đại. Mà chính nó là một lý do thực sự tốt để sử dụng uint thay vì ushort. Nhận được lực kéo ra khỏi ushort là khó khăn, bạn sẽ cần rất nhiều người trong số họ trước khi chi phí của thao tác chúng ra trọng lượng tiết kiệm bộ nhớ. Không chỉ vì thông số CLI giới hạn, lõi x86 mất thêm một chu kỳ CPU để tải một giá trị 16 bit vì byte tiền tố toán hạng trong mã máy. Không thực sự chắc chắn nếu đó vẫn là trường hợp ngày hôm nay, nó được sử dụng để trở lại khi tôi vẫn chú ý đến các chu kỳ đếm. Một con chó năm trước.


Lưu ý rằng bạn có thể cảm thấy tốt hơn về các trình biên dịch xấu xí và nguy hiểm này bằng cách tạo trình biên dịch VB.NET tạo. Vì vậy, bạn nhận được một OverflowException khi diễn viên hóa ra là không khôn ngoan. Sử dụng Project> Properties> Build tab> Advanced button> đánh dấu vào hộp kiểm "Check for overflow/underflow number". Chỉ dành cho bản dựng Gỡ lỗi. Tại sao hộp kiểm này không được tự động bật bởi mẫu dự án là một câu hỏi rất băn khoăn khác, một quyết định đã được thực hiện cách đây quá lâu.

+0

"Đó không phải là khó khăn, chỉ cần chuyển đổi ushort thành một uint, một chuyển đổi luôn hợp lệ." Thật vậy, đó là những gì tôi nghĩ là tốt, nhưng nó sẽ không được chuyển đổi thành một uint, nhưng đến một int, mà không ai có thể thực sự giải thích được nêu ra. Một câu trả lời rất hay, mặc dù! – lesderid

+1

Vâng, tôi đã xóa nó, cố định. Nhìn vào cái bàn tôi chỉ vào, nó không có các loại unsigned. –

+0

Ah, tôi hiểu rồi. Tôi đoán rằng câu trả lời cho câu hỏi của tôi! Tôi đang tặng thưởng cho Habib.OSU tiền thưởng, khi anh ấy trả lời đầy đủ câu hỏi ban đầu, trước khi tôi chỉnh sửa nó. – lesderid

15
ushort x = 5, y = 12; 

Các câu lệnh gán sau đây sẽ tạo ra một lỗi biên dịch, vì biểu thức số học ở phía bên tay phải của toán tử gán để đánh giá int theo mặc định.

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

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

EDIT:
Trong trường hợp của phép tính số học trên ushort, các toán hạng được chuyển đổi thành một loại mà có thể giữ tất cả các giá trị. Vì vậy, tràn có thể tránh được. Các toán tử có thể thay đổi theo thứ tự int, uint, long và ulong. Vui lòng xem C# Language Specification Trong tài liệu này, đi tới phần 4.1.5 Các loại tích phân (khoảng trang 80 trong tài liệu từ). Ở đây bạn sẽ tìm thấy:

Đối với nhị phân +, -, *, /,%, &, ^, |, ==, =,>, <,> =, và < = nhà khai thác, các toán hạng được chuyển thành loại T, trong đó T là đầu tiên của int, uint, long và ulong mà có thể đại diện cho tất cả giá trị có thể có của cả hai toán hạng. Sau đó, hoạt động được thực hiện bằng cách sử dụng độ chính xác của loại T và loại kết quả là T (hoặc bool cho các toán tử quan hệ ). Nó không được phép cho một toán hạng có kích thước là loại dài và loại kia là loại ulong với toán tử nhị phân.

Eric Lipper đã tuyên bố trong một số học question

không bao giờ được thực hiện trong quần short trong C#. Số học có thể được thực hiện trong ints, uints, longs và ulongs, nhưng số học không bao giờ được thực hiện trong quần short. Quần short quảng bá cho int và số học được thực hiện bằng int, vì như Tôi đã nói trước đây, phần lớn các phép tính số học phù hợp với một int. Đại đa số không phù hợp với một đoạn ngắn. Số học ngắn là có thể chậm hơn trên phần cứng hiện đại được tối ưu hóa cho ints và số học ngắn không chiếm ít không gian hơn; nó sẽ là được thực hiện bằng ints hoặc longs trên chip.

+0

Điều đó vẫn không giải thích tại sao nó trở thành một int và không phải là một uint. – lesderid

+1

@lesderid, điều này là do toán tử + quá tải ưu tiên được xác định trong đặc tả C# (7.8.4, trang 192). Trong ký tự quá tải đầu tiên này là toán tử int + (int x, int y) và sau đó toán tử uint của nó + (uint x, uint y). Vì vậy, đó là lý do tại sao nó trở thành int không uint – Habib

5

Từ C đặc tả # ngôn ngữ:

7.3.6.2 Khuyến mãi số nhị phân Khuyến mãi số nhị phân xảy ra cho các toán hạng của các toán tử được xác định trước +, -, *, /,%, &, |, ^, ==,! =,>, <,> = và < = nhị phân. Xúc tiến số nhị phân ngầm chuyển đổi cả hai toán hạng thành một kiểu chung, trong trường hợp các toán tử không quan hệ, cũng trở thành kiểu kết quả của phép toán. Khuyến mãi số nhị phân bao gồm việc áp dụng các quy tắc sau, theo thứ tự chúng xuất hiện ở đây:

· Nếu toán hạng là kiểu thập phân, toán hạng khác được chuyển thành kiểu thập phân hoặc lỗi thời gian liên kết xảy ra nếu toán hạng khác là kiểu float hoặc double.

· Nếu không, nếu toán hạng là loại gấp đôi, toán hạng khác sẽ được chuyển thành loại gấp đôi.

· Nếu không, nếu toán hạng là kiểu float, toán hạng khác sẽ được chuyển thành kiểu float. Nếu không, nếu toán hạng là loại ulong, toán hạng khác được chuyển thành loại ulong, hoặc lỗi thời gian liên kết xảy ra nếu toán hạng khác có kiểu sbyte, short, int hoặc long.

· Nếu không, nếu toán hạng là loại dài, toán hạng khác sẽ được chuyển thành loại dài.

· Nếu không, nếu toán hạng là loại uint và toán hạng khác thuộc loại sbyte, short hoặc int, cả hai toán hạng sẽ được chuyển đổi thành loại dài.

· Nếu không, nếu toán hạng là loại uint, toán hạng khác sẽ được chuyển thành kiểu uint.

· Nếu không, cả hai toán hạng sẽ được chuyển thành loại int.

+0

Điều đó thực sự xác nhận những phát hiện của tôi, nhưng nó không thực sự giải thích tại sao nó xảy ra. – lesderid

+1

@lesderid: Tôi không hiểu. Điều này xảy ra vì bạn đang sử dụng trình biên dịch tuân thủ thông số C#. Có lẽ bạn đang hỏi lý do đằng sau phần 7.3.6.2, và tại sao các quy tắc dừng lại ở int và không tiếp tục đi đến các loại nhỏ hơn? –

+1

Vâng vâng, tôi quan tâm đến lý do tại sao spec nói rằng cả hai toán hạng nên được chuyển đổi thành int. Tôi chỉ đơn thuần thêm các lỗi trình biên dịch để làm rõ và bởi vì tôi đã không chắc chắn nếu đây là một spec hoặc một quyết định thực hiện. – lesderid

3

Không có lý do gì được dự định. Đây chỉ là một hiệu ứng hoặc áp dụng các quy tắc về độ phân giải quá tải cho biết rằng quá tải đầu tiên cho các tham số của nó có một chuyển đổi ngầm định phù hợp với các đối số, quá tải sẽ được sử dụng.

Điều này được nêu trong thư mục C# Thông số kỹ thuật, phần 7.3.6 như sau:

xúc tiến Numeric không phải là một cơ chế riêng biệt, mà là một hiệu ứng của việc áp dụng giải quyết tình trạng quá tải cho các nhà khai thác được xác định trước.

Nó đi vào minh họa bằng một ví dụ:

Như một ví dụ về chương trình khuyến mãi số, xem xét việc triển khai xác định trước của nhà điều hành * nhị phân:

int operator * (int x, int y);

toán tử uint * (uint x, uint y);

nhà điều hành dài * (dài x, dài y);

nhà điều hành ulong * (ulong x, ulong y);

toán tử float * (float x, float y);

toán tử đôi * (double x, double y);

toán tử thập phân * (thập phân x, thập phân y);

Khi quy tắc giải quyết quá tải (§7.5.3) được áp dụng cho nhóm nhà khai thác này, hiệu quả là chọn toán tử đầu tiên cho chuyển đổi tiềm ẩn tồn tại từ các loại toán hạng. Ví dụ, đối với hoạt động b * s, trong đó b là một byte và s là một độ phân giải ngắn, quá tải chọn toán tử * (int, int) làm toán tử tốt nhất.

2

Câu hỏi của bạn trên thực tế, hơi phức tạp một chút. Lý do tại sao đặc điểm kỹ thuật này là một phần của ngôn ngữ là ... bởi vì họ đã quyết định khi họ tạo ra ngôn ngữ. Tôi biết điều này nghe có vẻ như một câu trả lời đáng thất vọng, nhưng đó chỉ là nó như thế nào.


Tuy nhiên, câu trả lời thực có thể liên quan đến nhiều quyết định ngữ cảnh trong ngày 1999-2000. Tôi chắc chắn rằng đội đã tạo ra C# đã có những cuộc tranh luận khá mạnh mẽ về tất cả các chi tiết ngôn ngữ đó.

  • ...
  • C# được thiết kế để trở thành một, hiện đại, có mục đích chung, hướng đối tượng ngôn ngữ lập trình đơn giản.
  • Tính di động của mã nguồn là rất quan trọng, cũng như tính di động của lập trình viên, đặc biệt đối với những lập trình viên đã quen thuộc với C và C++.
  • Hỗ trợ quốc tế hóa là rất quan trọng.
  • ...

The quote above is from Wikipedia C#

Tất cả những mục tiêu thiết kế có thể đã ảnh hưởng đến quyết định của họ. Ví dụ, trong năm 2000, hầu hết hệ thống đã có bản địa 32 bit, vì vậy họ có thể đã quyết định giới hạn số lượng nhỏ hơn, vì nó sẽ được chuyển đổi trên 32 bit khi thực hiện các phép toán số học. Nói chung là chậm hơn.

Tại thời điểm đó, bạn có thể hỏi tôi; nếu có chuyển đổi tiềm ẩn trên các loại đó thì tại sao chúng lại bao gồm chúng? Một trong những mục tiêu thiết kế của họ, như được trích dẫn ở trên, là tính di động.

Vì vậy, nếu bạn cần viết trình bao bọc C# xung quanh chương trình C hoặc C++ cũ, bạn có thể cần loại đó để lưu trữ một số giá trị. Trong trường hợp đó, những loại đó khá tiện dụng.

Đó là quyết định mà Java không đưa ra. Ví dụ, nếu bạn viết một chương trình Java tương tác với một chương trình C++ theo cách mà bạn nhận được các giá trị ushort, thì Java chỉ có ngắn (được ký), vì vậy bạn không thể dễ dàng gán một cái cho nhau và mong đợi các giá trị đúng.

Tôi cho phép bạn đặt cược, loại có sẵn tiếp theo có thể nhận được giá trị như vậy trong Java là int (32 bit của khóa học). Bạn vừa tăng gấp đôi bộ nhớ của mình ở đây. Mà có thể không phải là một việc lớn, thay vào đó bạn phải khởi tạo một mảng của 100 000 yếu tố.

Thực tế, chúng ta phải nhớ rằng những quyết định đó được đưa ra bằng cách nhìn vào quá khứ và tương lai để cung cấp sự chuyển giao suôn sẻ từ người này sang người khác.

Nhưng bây giờ tôi cảm thấy rằng tôi đang phân tách câu hỏi ban đầu.


Vì vậy, câu hỏi của bạn là câu hỏi hay và hy vọng tôi có thể mang lại một số câu trả lời cho bạn, mặc dù tôi biết đó có thể không phải là điều bạn muốn nghe.

Nếu bạn muốn, bạn thậm chí có thể đọc thêm về thông số C#, liên kết bên dưới. Có một số tài liệu thú vị có thể thú vị cho bạn.

Integral types

The checked and unchecked operators

Implicit Numeric Conversions Table

Bằng cách này, tôi tin rằng có lẽ bạn nên thưởng cho habib-osu cho nó, kể từ khi ông được cung cấp một câu trả lời khá tốt cho câu hỏi ban đầu với một liên kết thích hợp. :)

Kính trọng

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