Tại sao Java không hỗ trợ ints không dấu?


374

Tại sao Java không bao gồm hỗ trợ cho các số nguyên không dấu?

Tôi dường như là một thiếu sót kỳ lạ, vì họ cho phép một người viết mã ít có khả năng tạo ra tràn vào đầu vào lớn bất ngờ.

Hơn nữa, sử dụng số nguyên không dấu có thể là một hình thức tự viết tài liệu, vì chúng chỉ ra rằng giá trị mà số nguyên không dấu được dự định giữ không bao giờ được coi là âm.

Cuối cùng, trong một số trường hợp, số nguyên không dấu có thể hiệu quả hơn cho các hoạt động nhất định, chẳng hạn như phân chia.

Nhược điểm bao gồm những gì?


137
Tôi không biết nhưng điều đó làm tôi khó chịu; ví dụ như viết mã mạng theo cách này khó hơn nhiều.
Tamas Czinege

20
Tôi ước chỉ có hai loại trong ngôn ngữ / cơ sở dữ liệu / ... thế giới: số và chuỗi :)
Liao

5
Viết mã mạng không khó hơn nhiều. BTW InputStream.read (), trả về một byte không dấu, chẳng hạn như một ký hiệu đã ký, vì vậy ví dụ mạng là một IMHO nhầm lẫn. Điều khó hiểu duy nhất của bạn là bạn cho rằng việc viết một giá trị đã ký là khác với việc viết một giá trị không dấu. tức là nếu bạn không thực sự biết điều gì đang xảy ra ở mức byte.
Peter Lawrey

19
@ZachSaw - Tôi cũng đã thực hiện gấp đôi khi thấy một nhà thiết kế ngôn ngữ thực hiện trích dẫn đó. Không có gì đơn giản hơn một số nguyên không dấu. Số nguyên đã ký rất phức tạp. Đặc biệt khi bạn xem xét việc vặn bit ở cấp độ bóng bán dẫn. Và làm thế nào để thay đổi số nguyên đã ký? Tôi đã phải kết luận rằng người thiết kế Java có vấn đề nghiêm trọng trong việc hiểu logic boolean.
PP.

8
Đối với tôi, việc xử lý hình ảnh trở nên khó khăn hơn với hình ảnh bytekhông thể đưa ra 140mức xám thẳng mà là -116bạn cần phải & 0xffcó được giá trị chính xác.
Matthieu

Câu trả lời:


193

Đây là từ một cuộc phỏng vấn với Gosling và những người khác , về sự đơn giản:

Gosling: Đối với tôi là một nhà thiết kế ngôn ngữ, điều mà tôi không thực sự coi mình như những ngày này, điều "đơn giản" thực sự có nghĩa là gì, tôi có thể mong đợi J. Random Developer giữ thông số kỹ thuật trong đầu. Định nghĩa đó nói rằng, chẳng hạn, Java không - và trên thực tế, rất nhiều ngôn ngữ này kết thúc với rất nhiều trường hợp góc, những điều mà không ai thực sự hiểu. Kiểm tra bất kỳ nhà phát triển C nào về unsign, và bạn sẽ sớm phát hiện ra rằng hầu như không có nhà phát triển C nào thực sự hiểu những gì diễn ra với unsign, số học không dấu là gì. Những điều như thế làm cho C phức tạp. Phần ngôn ngữ của Java là, tôi nghĩ, khá đơn giản. Các thư viện bạn phải tìm kiếm.


222
Tôi sẽ không đồng ý với Gosling ở đây với một ví dụ cụ thể (từ CLR không hơn không kém). Điều gì khó hiểu hơn khi cung cấp cho Mảng một giá trị độ dài nguyên đã ký hoặc độ dài không dấu? Một mảng không thể có độ dài âm nhưng API của chúng tôi cho thấy điều đó là có thể.
JaredPar

18
Đối số làm cho Java trở nên đơn giản là một phần của những gì khiến chúng ta rơi vào tình trạng lộn xộn với việc thiếu các khuôn mẫu mà cuối cùng chúng được đưa vào ngôn ngữ vì các lựa chọn thay thế rất cồng kềnh. Tôi nghĩ rằng người ta có thể hỗ trợ ints không dấu với một lớp thích hợp, tuy nhiên, nó không cần prims
Uri

59
Nếu Java cần các số nguyên không dấu vì các chỉ số Mảng không thể âm, thì nó cũng cần các phần phụ (a la Pascal) vì chỉ mục mảng không thể lớn hơn kích thước mảng.
Wayne Conrad

81
Được rồi, ông chỉ nói những lợi thế của việc không có loại không dấu. Bây giờ hãy tính những nhược điểm ...
Moshe Revah 7/12/2016

83
Tôi thích mã đơn giản hơn ngôn ngữ đơn giản. Đó là lý do tại sao tôi ghét Java.
Pijusn

50

Đọc giữa các dòng, tôi nghĩ logic là như thế này:

  • nói chung, các nhà thiết kế Java muốn đơn giản hóa các tiết mục của các kiểu dữ liệu có sẵn
  • cho các mục đích hàng ngày, họ cảm thấy rằng nhu cầu phổ biến nhất là các loại dữ liệu đã ký
  • để thực hiện một số thuật toán nhất định, đôi khi số học không dấu là cần thiết, nhưng loại lập trình viên sẽ thực hiện các thuật toán đó cũng sẽ có kiến ​​thức để "làm việc tròn" thực hiện số học không dấu với các loại dữ liệu đã ký

Hầu hết, tôi muốn nói rằng đó là một quyết định hợp lý. Có thể, tôi sẽ có:

  • tạo byte không dấu, hoặc ít nhất đã cung cấp một lựa chọn thay thế được ký / không dấu, có thể với các tên khác nhau, cho một loại dữ liệu này (làm cho nó được ký là tốt cho tính nhất quán, nhưng khi nào bạn cần một byte đã ký?)
  • thực hiện với 'short' (lần cuối bạn sử dụng số học có chữ ký 16 bit là khi nào?)

Tuy nhiên, với một chút loại bỏ, các thao tác trên các giá trị không dấu lên đến 32 bit không có gì xấu và hầu hết mọi người không cần phân chia hoặc so sánh 64 bit không dấu.


2
Tôi cũng rất thích có các byte không dấu, nhưng tôi nghi ngờ lợi thế của tính nhất quán hoàn toàn giữa các loại số nguyên vượt trội hơn sự tiện lợi mà các byte không dấu sẽ mang lại.
Alan Moore

64
"Đối với các mục đích hàng ngày, họ cảm thấy rằng nhu cầu phổ biến nhất là các loại dữ liệu đã ký". Trong mã C ++ của mình, tôi thường xuyên thấy mình suy nghĩ "Tại sao tôi lại sử dụng một số nguyên có chữ ký ở đây thay vì số không dấu?!". Tôi có cảm giác rằng "đã ký" là ngoại lệ chứ không phải là quy tắc (tất nhiên, nó phụ thuộc vào tên miền, nhưng có một lý do tại sao các số nguyên dương được gọi là số tự nhiên ;-)).
Luc Touraille

15
đưa ra lời kêu gọi cho các byte không dấu, khi thực hiện xử lý hình ảnh, giả sử các byte không dấu (như vậy), khiến tôi mất hàng giờ để gỡ lỗi.
Helin Wang

7
bạn sẽ ngạc nhiên về mức độ thường xuyên shortđược sử dụng - thuật toán defltate / gzip / Inflate là 16 bit và chúng phụ thuộc rất nhiều vào quần short ... hoặc ít nhất là short[][thừa nhận chúng là bản địa - nhưng java ẩn của thuật toán mang dữ liệu terrabyte]. Cái sau ( short[]) có lợi thế đáng int[]kể vì nó chiếm ít bộ nhớ hơn và bộ nhớ ít hơn = thuộc tính bộ đệm tốt hơn, hiệu năng tốt hơn nhiều.
bestsss

8
Mặc dù trong một ứng dụng cụ thể, bạn nên đo xem việc sử dụng quần short có mang lại cho bạn hiệu suất tốt hơn thay vì cho rằng nó là đúng. Có thể là trò đùa-pokery thêm cần thiết để thao tác với quần short thay vì ints (thường là loại mà bộ xử lý 'thích sử dụng') thực sự có thể gây bất lợi cho hiệu suất trong một ứng dụng cụ thể. Không phải luôn luôn, nhưng bạn nên kiểm tra, không giả định.
Neil Coffey

19

Đây là một câu hỏi cũ hơn và pat đã đề cập ngắn gọn về char, tôi chỉ nghĩ rằng tôi nên mở rộng điều này cho những người khác sẽ xem xét điều này trên đường. Chúng ta hãy xem xét kỹ hơn về các kiểu nguyên thủy của Java:

byte - Số nguyên có chữ ký 8 bit

short - Số nguyên có chữ ký 16 bit

int - Số nguyên có chữ ký 32 bit

long - Số nguyên có chữ ký 64 bit

char - Ký tự 16 bit (số nguyên không dấu)

Mặc dù charkhông hỗ trợ unsignedsố học, nhưng về cơ bản nó có thể được coi là một unsignedsố nguyên. Bạn sẽ phải đưa các phép toán số học trở lại một cách rõ ràng char, nhưng nó cung cấp cho bạn một cách để xác định các unsignedsố.

char a = 0;
char b = 6;
a += 1;
a = (char) (a * b);
a = (char) (a + b);
a = (char) (a - 16);
b = (char) (b % 3);
b = (char) (b / a);
//a = -1; // Generates complier error, must be cast to char
System.out.println(a); // Prints ? 
System.out.println((int) a); // Prints 65532
System.out.println((short) a); // Prints -4
short c = -4;
System.out.println((int) c); // Prints -4, notice the difference with char
a *= 2;
a -= 6;
a /= 3;
a %= 7;
a++;
a--;

Có, không có hỗ trợ trực tiếp cho các số nguyên không dấu (rõ ràng, tôi sẽ không phải chuyển hầu hết các hoạt động của mình trở lại char nếu có hỗ trợ trực tiếp). Tuy nhiên, chắc chắn tồn tại một kiểu dữ liệu nguyên thủy không dấu. Tôi cũng muốn thấy một byte không dấu, nhưng tôi đoán tăng gấp đôi chi phí bộ nhớ và thay vào đó sử dụng char là một lựa chọn khả thi.


Biên tập

Với JDK8, có các API mới cho LongIntegercung cấp các phương thức của trình trợ giúp khi xử lý longintcác giá trị là các giá trị không dấu.

  • compareUnsigned
  • divideUnsigned
  • parseUnsignedInt
  • parseUnsignedLong
  • remainderUnsigned
  • toUnsignedLong
  • toUnsignedString

Ngoài ra, Guava cung cấp một số phương thức trợ giúp để thực hiện những điều tương tự tại các loại số nguyên giúp thu hẹp khoảng cách còn lại do thiếu hỗ trợ riêng cho unsignedsố nguyên.


2
Nhưng tuy nhiên, charquá nhỏ để hỗ trợ longsố học, ví dụ.

3
Đây có thể là một bất lợi của Java

Hy vọng rằng họ hỗ trợ các giá trị Unsign cho byte. Làm cho mọi thứ dễ dàng hơn.
mixz

15

Java có các kiểu không dấu hoặc ít nhất một kiểu: char là một dấu ngắn không dấu. Vì vậy, bất cứ lý do gì mà Gosling ném lên, đó thực sự chỉ là sự thiếu hiểu biết của anh ta tại sao không có loại không dấu nào khác.

Ngoài ra các loại ngắn: quần short được sử dụng tất cả các thời gian cho đa phương tiện. Lý do là bạn có thể ghép 2 mẫu trong một dấu dài 32 bit không dấu và vectơ nhiều thao tác. Điều tương tự với dữ liệu 8 bit và byte không dấu. Bạn có thể phù hợp với 4 hoặc 8 mẫu trong một đăng ký để vector hóa.


37
Vâng, tôi chắc chắn rằng Gosling rất không biết gì về Java so với bạn.
jakeboxer

Java có cho phép số học được thực hiện trực tiếp trên các đại lượng byte không dấu hay các giá trị luôn được thăng cấp không? Có một loại không dấu để lưu trữ, nhưng luôn thực hiện số học trên một loại đã ký đủ lớn để chứa nó hoạt động tốt về mặt ngữ nghĩa, nhưng sẽ khiến các hoạt động trên các loại không dấu có cùng kích thước với số nguyên "bình thường" đắt hơn.
supercat

2
Đó là phong cách xấu để sử dụng charcho bất cứ điều gì trừ nhân vật.
starblue

5
@starblue Tất nhiên là vậy, nhưng đó là một hack để vượt qua giới hạn của ngôn ngữ
Basic

14

Ngay sau khi ints đã ký và không dấu được trộn lẫn trong một biểu thức, mọi thứ bắt đầu trở nên lộn xộn và bạn có thể sẽ mất thông tin. Việc giới hạn Java đối với các int đã ký chỉ thực sự làm sáng tỏ mọi thứ. Tôi rất vui vì tôi không phải lo lắng về toàn bộ doanh nghiệp đã ký / chưa ký, mặc dù đôi khi tôi bỏ lỡ bit thứ 8 trong một byte.


12
Đối với việc trộn có dấu / không dấu: Bạn có thể có các kiểu không dấu, nhưng không cho phép trộn (hoặc yêu cầu phôi rõ ràng). Tuy nhiên, không rõ liệu nó có cần thiết hay không.
sleske

2
Trong C ++, bạn phải rắc static_castnhiều xung quanh để trộn chúng. Nó thực sự là lộn xộn.
Raedwald

4
Bit thứ 8 là ở đó, nó chỉ cố gắng ẩn mình như là dấu hiệu.
starblue

Mọi thứ chỉ trở nên lộn xộn với các loại 32 bit hoặc lớn hơn. Tôi thấy không có lý do gì Java không nên byteđược ký như trong Pascal.
supercat

12
Hãy đến gặp tôi khi bạn gặp vấn đề với việc xử lý hình ảnh trong Java, nơi bạn mong đợi các byte sẽ không được ký. Sau đó, bạn sẽ biết rằng việc & 0xFFquảng cáo từng byte làm cho mã trở nên rắc rối hơn.
bit2shift

12

http://skimecoder.blogspot.com/2006/09/java-tutorials-why-no-unsign.html

Anh chàng này nói bởi vì tiêu chuẩn C định nghĩa các hoạt động liên quan đến ints không dấu và đã ký được coi là không dấu. Điều này có thể khiến các số nguyên có dấu âm cuộn quanh thành một số nguyên lớn không dấu, có khả năng gây ra lỗi.


34
Số nguyên có chữ ký Java cuộn quanh, quá. Tôi không thấy quan điểm của bạn.
foo

8
@foo: Số nguyên đã ký phải trở nên lớn trước khi chúng gây ra sự cố. Ngược lại, trong C, người ta có thể gặp vấn đề khi so sánh bất kỳ số nguyên âm nào - thậm chí - với -1bất kỳ số lượng không dấu nào - thậm chí bằng không.
supercat

Thật tệ khi Java không thể bao gồm các loại không dấu, nhưng với một bộ chuyển đổi và toán tử hỗn hợp hạn chế (hơi giống với cách mà trong C, người ta có thể thêm 5 vào một con trỏ, nhưng người ta không thể so sánh một con trỏ với 5) . Ý tưởng rằng việc sử dụng một toán tử trên các loại hỗn hợp khi tồn tại một diễn viên ngầm, sẽ buộc sử dụng ngầm định đó (và sử dụng loại kết quả làm kiểu kết quả) nằm ở trung tâm của rất nhiều quyết định thiết kế đáng ngờ trong cả .NET và Java.
supercat

4
Không nói về câu trả lời của bạn, nhưng có -1độ tuổi "không xác định" (như bài báo gợi ý) là một trong những ví dụ kinh điển về "mùi mã" . Chẳng hạn, nếu bạn muốn tính toán "Alice bao nhiêu tuổi hơn Bob?" Và A = 25 và B = -1, bạn sẽ nhận được câu trả lời ±26đơn giản là sai. Việc xử lý đúng các giá trị chưa biết là một số loại Option<TArg>khi nào Some(25) - Nonesẽ quay trở lại None.
bytebuster

11

Tôi nghĩ rằng Java là tốt như nó là, thêm không dấu sẽ làm phức tạp nó mà không đạt được nhiều. Ngay cả với mô hình số nguyên được đơn giản hóa, hầu hết các lập trình viên Java cũng không biết các kiểu số cơ bản hoạt động như thế nào - chỉ cần đọc cuốn sách Java Puzzlers để xem những hiểu lầm nào bạn có thể nắm giữ.

Đối với lời khuyên thực tế:

  • Nếu giá trị của bạn có kích thước tùy ý và không phù hợp int, hãy sử dụng long. Nếu chúng không phù hợp để longsử dụng BigInteger.

  • Chỉ sử dụng các loại nhỏ hơn cho mảng khi bạn cần tiết kiệm không gian.

  • Nếu bạn cần chính xác 64/32/16/8 bit, hãy sử dụng long/ int/ short/ bytevà ngừng lo lắng về bit dấu, ngoại trừ việc phân chia, so sánh, dịch chuyển phải và truyền.

Xem thêm câu trả lời này về "chuyển một trình tạo số ngẫu nhiên từ C sang Java".


5
Có, để chuyển đổi quyền, bạn phải chọn giữa >>>>>cho chữ ký và không dấu, tương ứng. Dịch chuyển sang trái là không có vấn đề.
starblue

1
@starblue Trên thực tế >>>không hoạt động cho shortbyte. Ví dụ, (byte)0xff>>>1sản lượng 0x7fffffffhơn là 0x7f. Một ví dụ khác: byte b=(byte)0xff; b>>>=1;sẽ dẫn đến b==(byte)0xff. Tất nhiên bạn có thể làm b=(byte)(b & 0xff >> 1);nhưng điều này thêm một thao tác nữa (bitwise &).
CITBL

7
"... Ngay cả với mô hình đơn giản hóa, hầu hết các lập trình viên Java cũng không biết các kiểu số cơ bản hoạt động như thế nào ..." Một cái gì đó trong tôi chỉ phẫn nộ với một ngôn ngữ nhắm vào mẫu số chung thấp nhất.
Cơ bản

Dòng mở đầu trong câu trả lời của bạn, về sự phức tạp hơn và thu được ít hơn, chính xác là những gì tôi đã xây dựng trong bài viết của mình 6 năm sau: nayuki.io/page/unign-int-considered-harmful-for-java
Nayuki

1
@Nayuki Bài viết của bạn rất hay. Chỉ có một nhận xét nhỏ, tôi sẽ sử dụng thêm 0x80000000 cho các toán tử so sánh thay vì XOR, bởi vì nó giải thích lý do tại sao nó hoạt động, nó chuyển vùng tiếp giáp trong đó phép so sánh xảy ra từ -MAXINT đến 0. Hiệu ứng của nó hoàn toàn giống nhau.
starblue

6

Với JDK8, nó có một số hỗ trợ cho họ.

Chúng tôi có thể vẫn thấy sự hỗ trợ đầy đủ của các loại không dấu trong Java bất chấp những lo ngại của Gosling.


12
aka "Vì vậy, mọi người thực sự sử dụng nó và chúng tôi đã sai khi không bao gồm nó để bắt đầu" - nhưng chúng tôi vẫn không hoàn toàn tin tưởng các nhà phát triển Java để biết liệu một biến có được ký hay không - vì vậy chúng tôi sẽ không triển khai chúng trong VM hoặc dưới dạng các loại tương đương với anh em họ đã ký.
Cơ bản

6

Tôi biết bài này quá cũ; tuy nhiên, đối với sở thích của bạn, trong Java 8 trở lên, bạn có thể sử dụng intkiểu dữ liệu để biểu thị một số nguyên 32 bit không dấu, có giá trị tối thiểu là 0 và giá trị tối đa là 2 32 1. Sử dụng Integerlớp để sử dụng intkiểu dữ liệu như một số nguyên không dấu và các phương thức tĩnh như compareUnsigned(), divideUnsigned()v.v. đã được thêm vào Integerlớp để hỗ trợ các phép toán số học cho các số nguyên không dấu.


4

Tôi đã nghe những câu chuyện rằng chúng sẽ được đưa vào gần với bản phát hành Java gốc. Oak là tiền thân của Java và trong một số tài liệu cụ thể có đề cập đến các giá trị được gán. Thật không may, những điều này không bao giờ làm cho nó thành ngôn ngữ Java. Theo như bất cứ ai đã có thể tìm ra họ chỉ không được thực hiện, có thể là do hạn chế về thời gian.


Điều này sẽ ổn thôi ... ngoại trừ bằng chứng từ cuộc phỏng vấn của Gosling ngụ ý rằng các số nguyên không dấu (ngoài char) bị bỏ đi vì các nhà thiết kế nghĩ rằng chúng là một ý tưởng tồi ... đưa ra các mục tiêu của ngôn ngữ.
Stephen C

Đó là một ý tưởng tốt không bao giờ đặt quá nhiều giá trị trong các tuyên bố của nhân chứng, nếu bằng chứng tài liệu cũng có trong tay.
user7610

4

Tôi đã từng tham gia khóa học C ++ với một người trong ủy ban tiêu chuẩn C ++, ngụ ý rằng Java đã đưa ra quyết định đúng đắn để tránh có số nguyên không dấu vì (1) hầu hết các chương trình sử dụng số nguyên không dấu có thể làm tốt với số nguyên đã ký và điều này tự nhiên hơn các điều khoản về cách mọi người nghĩ và (2) sử dụng số nguyên không dấu dẫn đến rất nhiều vấn đề dễ tạo nhưng khó gỡ lỗi như tràn số học số nguyên và mất bit đáng kể khi chuyển đổi giữa các loại đã ký và không dấu. Nếu bạn trừ nhầm 1 từ 0 bằng cách sử dụng các số nguyên có chữ ký, nó thường nhanh chóng khiến chương trình của bạn gặp sự cố và giúp tìm lỗi dễ dàng hơn nếu nó bao quanh đến 2 ^ 32 - 1, và trình biên dịch và các công cụ phân tích tĩnh và kiểm tra thời gian chạy phải giả sử bạn biết những gì bạn đang làm kể từ khi bạn chọn sử dụng số học không dấu. Cũng thế,

Từ lâu, khi bộ nhớ bị hạn chế và bộ xử lý không tự động hoạt động trên 64 bit cùng một lúc, mỗi bit được tính nhiều hơn rất nhiều, do đó, việc ký kết với các byte hoặc quần short không dấu thực sự quan trọng hơn rất nhiều và rõ ràng là quyết định thiết kế đúng đắn. Ngày nay, chỉ cần sử dụng một int đã ký là quá đủ trong hầu hết các trường hợp lập trình thông thường và nếu chương trình của bạn thực sự cần sử dụng các giá trị lớn hơn 2 ^ 31 - 1, bạn thường chỉ muốn một thời gian dài. Khi bạn đã vào lãnh thổ của việc sử dụng lâu dài, việc đưa ra lý do tại sao bạn thực sự không thể có được với 2 ^ 63 - 1 số nguyên dương thậm chí còn khó hơn. Bất cứ khi nào chúng tôi đi đến bộ xử lý 128 bit, vấn đề sẽ ít hơn.


2

Câu hỏi của bạn là "Tại sao Java không hỗ trợ ints không dấu"?

Và câu trả lời của tôi cho câu hỏi của bạn là Java muốn rằng tất cả các kiểu nguyên thủy của nó: byte , char , short , intlong phải được coi là byte , word , dwordqword , tương tự như trong lắp ráp và các toán tử Java được hoạt động trên tất cả các loại nguyên thủy của nó ngoại trừ char , nhưng chỉ trên char chúng không được ký 16 bit.

Vì vậy, các phương thức tĩnh giả sử là các hoạt động không dấu cũng cho cả 32 và 64 bit.

Bạn cần lớp cuối cùng, có phương thức tĩnh có thể được gọi cho dấu không dấu hoạt động .

Bạn có thể tạo lớp cuối cùng này, gọi nó là bất kỳ tên nào bạn muốn và thực hiện các phương thức tĩnh.

Nếu bạn không biết về cách thực hiện các phương thức tĩnh thì liên kết này có thể giúp bạn.

Theo tôi, Java là không giống với C ++ ở tất cả , nếu nó không hỗ trợ các kiểu unsigned hay khai thác quá tải, vì vậy tôi nghĩ rằng Java phải được coi là ngôn ngữ hoàn toàn khác nhau từ cả hai C ++ và từ C.

Nhân tiện, nó cũng hoàn toàn khác nhau trong tên của các ngôn ngữ.

Vì vậy, tôi không khuyên Java nên nhập mã tương tự như C và tôi không khuyên bạn nên nhập mã tương tự như C ++, vì khi đó trong Java bạn sẽ không thể làm những gì bạn muốn làm tiếp theo trong C ++, tức là mã sẽ không tiếp tục giống như C ++ và đối với tôi, điều này thật tệ khi viết mã như vậy, để thay đổi kiểu ở giữa.

Tôi cũng khuyên bạn nên viết và sử dụng các phương thức tĩnh cho các hoạt động đã ký, vì vậy bạn không thấy trong hỗn hợp mã của các toán tử và phương thức tĩnh cho cả các hoạt động đã ký và không dấu, trừ khi bạn chỉ cần các hoạt động được ký trong mã và không sao cả chỉ sử dụng các toán tử.

Ngoài ra tôi khuyên bạn nên tránh sử dụng các kiểu nguyên thủy ngắn , intdài , và sử dụng từ , dwordqword tương ứng, và bạn sẽ gọi các phương thức tĩnh cho các hoạt động không dấu và / hoặc các hoạt động được ký thay vì sử dụng các toán tử.

Nếu bạn chỉ thực hiện các hoạt động đã ký và chỉ sử dụng các toán tử trong mã, thì việc sử dụng các kiểu nguyên thủy này ngắn , intlong là ổn .

Trên thực tế từ , DWORDqword làm không tồn tại trong ngôn ngữ, nhưng bạn có thể tạo lớp mới cho mỗi và việc thực hiện của mỗi nên rất dễ dàng:

Lớp từ nắm giữ các loại nguyên thủy ngắn chỉ, lớp dword giữ kiểu nguyên thủy int chỉ và lớp qword giữ kiểu nguyên thủy dài mà thôi. Bây giờ tất cả các phương thức không dấu và phương thức đã ký là tĩnh hoặc không phải là lựa chọn của bạn, bạn có thể thực hiện trong mỗi lớp, tức là tất cả các hoạt động 16 bit cả không dấu và được ký bằng cách đặt tên ý nghĩa trên lớp từ , tất cả các hoạt động 32 bit đều không dấu và được ký bằng cách đặt tên ý nghĩa trên lớp dword và tất cả các hoạt động 64 bit cả không dấu và được ký bằng cách đặt tên ý nghĩa trên lớp qword .

Nếu bạn không muốn đưa ra quá nhiều tên khác nhau cho mỗi phương thức, bạn luôn có thể sử dụng quá tải trong Java, thật tốt khi đọc rằng Java cũng không loại bỏ điều đó!

Nếu bạn muốn các phương thức thay vì các toán tử cho các hoạt động được ký 8 bit và các phương thức cho các hoạt động không dấu 8 bit hoàn toàn không có toán tử, thì bạn có thể tạo lớp Byte (lưu ý rằng chữ cái đầu tiên 'B' là vốn, vì vậy đây không phải là kiểu byte nguyên thủy ) và thực hiện các phương thức trong lớp này.

Về việc chuyển theo giá trị và chuyển qua tham chiếu:

Nếu tôi không sai, giống như trong C #, các đối tượng nguyên thủy được truyền theo giá trị tự nhiên, nhưng đối tượng lớp được thông qua tham khảo một cách tự nhiên, do đó phương tiện mà đối tượng của kiểu Byte , từ , dwordqword sẽ được thông qua tham khảo và không phải bởi giá trị theo mặc định Tôi ước Java có các đối tượng struct như C #, vì vậy tất cả Byte , word , dwordqword có thể được triển khai thành struct thay vì class, do đó, theo mặc định, chúng được truyền theo giá trị chứ không phải theo tham chiếu theo mặc định, giống như bất kỳ đối tượng cấu trúc nào trong C #, giống như các kiểu nguyên thủy, được truyền theo giá trị và không theo tham chiếu theo mặc định, nhưng vì Java kém hơn C # và chúng tôi có để đối phó với điều đó, sau đó chỉ có các lớp và giao diện, được truyền bằng tham chiếu chứ không phải theo giá trị theo mặc định. Vì vậy, nếu bạn muốn truyền các đối tượng Byte , word , dwordqword theo giá trị chứ không phải bằng tham chiếu, giống như bất kỳ đối tượng lớp nào khác trong Java và cả trong C #, bạn sẽ chỉ cần sử dụng hàm tạo sao chép và đó là nó.

Đó là giải pháp duy nhất mà tôi có thể nghĩ đến. Tôi chỉ ước rằng tôi chỉ có thể gõ các kiểu nguyên thủy thành từ, từ và qword, nhưng Java không hỗ trợ typedef cũng như không sử dụng, không giống như C # hỗ trợ sử dụng , tương đương với typedef của C.

Về đầu ra:

Đối với cùng một chuỗi bit , bạn có thể in chúng theo nhiều cách: Dưới dạng nhị phân, dưới dạng thập phân (như ý nghĩa của% u trong C printf), dưới dạng bát phân (như ý nghĩa của% o trong C printf), dưới dạng thập lục phân (như ý nghĩa của% x trong C printf) và dưới dạng số nguyên (giống như ý nghĩa của% d trong C printf).

Lưu ý rằng C printf không biết loại biến được truyền dưới dạng tham số cho hàm, vì vậy printf chỉ biết loại của từng biến từ đối tượng char * được truyền cho tham số đầu tiên của hàm.

Vì vậy, trong mỗi lớp: Byte , word , dwordqword , bạn có thể thực hiện phương thức in và nhận chức năng của printf, mặc dù kiểu nguyên thủy của lớp được ký, bạn vẫn có thể in dưới dạng không dấu bằng cách tuân theo một số thuật toán liên quan hoạt động logic và dịch chuyển để có được các chữ số để in ra đầu ra.

Thật không may, liên kết tôi đưa cho bạn không chỉ ra cách triển khai các phương thức in này, nhưng tôi chắc chắn rằng bạn có thể google cho các thuật toán bạn cần để thực hiện các phương pháp in này.

Đó là tất cả những gì tôi có thể trả lời câu hỏi của bạn và đề nghị bạn.


MASM (trình biên dịch Microsoft) và Windows định nghĩa BYTE, WORD, DWORD, QWORD, là các loại không dấu. Đối với MASM, SBYTE, SWORD, SDWORD, SQWORD là các loại đã ký.
RCgldr

1

unsignedloại là tà ác.

Thực tế là trong C unsigned - intsản xuất unsignedthậm chí còn ác hơn.

Dưới đây là một ảnh chụp nhanh về vấn đề đã đốt cháy tôi hơn một lần:

// We have odd positive number of rays, 
// consecutive ones at angle delta from each other.
assert( rays.size() > 0 && rays.size() % 2 == 1 );

// Get a set of ray at delta angle between them.
for( size_t n = 0; n < rays.size(); ++n )
{
    // Compute the angle between nth ray and the middle one.
    // The index of the middle one is (rays.size() - 1) / 2,
    // the rays are evenly spaced at angle delta, therefore
    // the magnitude of the angle between nth ray and the 
    // middle one is: 
    double angle = delta * fabs( n - (rays.size() - 1) / 2 ); 

    // Do something else ...
}

Bạn đã nhận thấy lỗi chưa? Tôi thú nhận tôi chỉ nhìn thấy nó sau khi bước vào với trình gỡ lỗi.

Bởi vì nlà loại không dấu size_t, toàn bộ biểu thức n - (rays.size() - 1) / 2đánh giá là unsigned. Biểu thức đó được dự định là vị trí đã ký của ntia thứ từ vị trí giữa: tia thứ nhất từ ​​giữa ở bên trái sẽ có vị trí -1, tia thứ nhất ở bên phải sẽ có vị trí +1, v.v. lấy giá trị abs và nhân với deltagóc tôi sẽ có được góc giữa ntia thứ và tia giữa.

Thật không may cho tôi, biểu thức trên chứa ác không dấu và thay vì đánh giá, giả sử, -1, nó đã ước tính thành 2 ^ 32-1. Việc chuyển đổi tiếp theo để doubleniêm phong lỗi.

Sau một hoặc hai lỗi do sử dụng sai unsignedsố học, người ta phải bắt đầu tự hỏi liệu một bit thừa nhận được có đáng để thêm rắc rối không. Tôi đang cố gắng hết sức có thể để tránh sử dụng các unsignedloại trong số học, mặc dù vẫn sử dụng nó cho các hoạt động phi số học như mặt nạ nhị phân.


Việc thêm "dài không dấu" vào Java sẽ rất khó xử. Tuy nhiên, việc thêm các loại không dấu nhỏ hơn sẽ không gây ra vấn đề gì. Đặc biệt là các loại nhỏ hơn "int" có thể được xử lý dễ dàng bằng cách quảng cáo chúng thành "int" theo kiểu số rõ ràng và "int unsign" có thể được xử lý bằng cách nói rằng các hoạt động liên quan đến int đã ký và int không dấu sẽ thúc đẩy cả hai toán hạng thành "dài". Tình huống vấn đề duy nhất sẽ là các hoạt động liên quan đến một số lượng dài không dấu và số lượng đã ký, vì sẽ không có loại nào có khả năng đại diện cho tất cả các giá trị của cả hai toán hạng.
supercat

@supercat: nếu unsignedđược chuyển đổi thành intở mọi hoạt động thì việc sử dụng là unsignedgì? Nó sẽ không có bất kỳ chức năng phân biệt với short. Và nếu bạn chuyển đổi sang intchỉ về hoạt động hỗn hợp, chẳng hạn như unsigned+inthay unsigned+float, sau đó bạn vẫn còn có vấn đề ((unsigned)25-(unsigned)30)*1.0 > 0, mà là một nguyên nhân chính gây unsignedlỗi -related.
Michael

Nhiều hoạt động trên các loại không dấu sẽ thúc đẩy "dài". Yêu cầu phôi rõ ràng khi lưu trữ kết quả trở lại các loại không dấu sẽ gây ra nhiều phiền toái tương tự như tồn tại với ngắn và byte, nhưng nếu loại chủ yếu là định dạng lưu trữ thay vì định dạng tính toán thì không phải là vấn đề. Trong mọi trường hợp, các loại không dấu ngắn hơn "int" chỉ đơn giản là có thể quảng bá thành "int" mà không gặp khó khăn.
supercat

3
Tôi không thích câu trả lời này vì nó sử dụng đối số "số nguyên không dấu là xấu và không nên tồn tại vì chúng không bao giờ có thể được ký". Bất cứ ai đang cố gắng trừ đi một số nguyên không dấu nên biết điều này rồi. Và về khả năng đọc, C không được biết chính xác là dễ theo dõi. Hơn nữa, đối số (bán) "bit thừa không đáng để gặp thêm rắc rối" cũng rất yếu. Là xử lý lỗi thay vì exit(1);thực sự 'đáng giá thêm rắc rối'? Không thể mở các tệp lớn thực sự có giá trị bảo mật mà các lập trình viên java ít kinh nghiệm sẽ không gây rối khi sử dụng unsigned?
yyny

2
Điều xấu xa duy nhất tôi thấy trong mã này là n - (rays.size() - 1) / 2. Bạn phải luôn đóng khung các toán tử nhị phân vì trình đọc mã không cần phải thừa nhận bất cứ điều gì về thứ tự các thao tác trong chương trình máy tính. Chỉ vì chúng tôi thường nói a + b c = a + (b c) không có nghĩa là bạn có thể giả sử điều này khi đọc mã. Hơn nữa, tính toán nên được xác định bên ngoài vòng lặp để có thể kiểm tra nó mà không cần vòng lặp. Đây là một lỗi trong việc không đảm bảo các loại của bạn xếp hàng thay vì vấn đề về số nguyên không dấu. Trong C, tùy thuộc vào bạn để đảm bảo các loại của bạn xếp hàng.
Dmitry

0

Có một vài viên đá quý trong thông số 'C' mà Java đã bỏ vì những lý do thực tế nhưng đang dần trở lại với nhu cầu của nhà phát triển (đóng cửa, v.v.).

Tôi đề cập đến cái đầu tiên vì nó liên quan đến cuộc thảo luận này; sự tuân thủ của các giá trị con trỏ đến số học số nguyên không dấu. Và, liên quan đến chủ đề chủ đề này, khó khăn trong việc duy trì ngữ nghĩa Unsign trong thế giới đã ký của Java.

Tôi đoán rằng nếu ai đó muốn có một Dennis Ritchie thay đổi bản ngã để tư vấn cho đội ngũ thiết kế của Gosling thì họ đã đề nghị cho Signed là "số không ở vô cực", để tất cả các yêu cầu bù địa chỉ trước tiên sẽ thêm ALGEBRAIC RING SIZE của họ để làm giảm giá trị âm.

Bằng cách đó, bất kỳ phần bù nào được ném vào mảng không bao giờ có thể tạo ra SEGFAULT. Ví dụ, trong một lớp được đóng gói mà tôi gọi là RingArray của các nhân đôi cần hành vi không dấu - trong ngữ cảnh "vòng lặp tự xoay":

// ...
// Housekeeping state variable
long entrycount;     // A sequence number
int cycle;           // Number of loops cycled
int size;            // Active size of the array because size<modulus during cycle 0
int modulus;         // Maximal size of the array

// Ring state variables
private int head;   // The 'head' of the Ring
private int tail;   // The ring iterator 'cursor'
// tail may get the current cursor position
// and head gets the old tail value
// there are other semantic variations possible

// The Array state variable
double [] darray;    // The array of doubles

// somewhere in constructor
public RingArray(int modulus) {
    super();
    this.modulus = modulus;
    tail =  head =  cycle = 0;
    darray = new double[modulus];
// ...
}
// ...
double getElementAt(int offset){
    return darray[(tail+modulus+offset%modulus)%modulus];
}
//  remember, the above is treating steady-state where size==modulus
// ...

RingArray ở trên sẽ không bao giờ 'nhận' từ một chỉ số tiêu cực, ngay cả khi một người yêu cầu độc hại đã cố gắng. Hãy nhớ rằng, cũng có nhiều yêu cầu hợp pháp để yêu cầu các giá trị chỉ mục (âm) trước.

NB:% modulus bên ngoài tham chiếu các yêu cầu hợp pháp trong khi% modulus bên trong che giấu ác ý trắng trợn từ phủ định tiêu cực hơn -modulus. Nếu điều này đã từng xuất hiện trong Java + .. + 9 || 8 + .. + spec, sau đó, vấn đề thực sự sẽ trở thành một "lập trình viên không thể" tự xoay "FAULT".

Tôi chắc chắn cái gọi là "thiếu dấu" Java không dấu có thể được tạo ra với một lớp lót ở trên.

PS: Chỉ để cung cấp ngữ cảnh cho công việc dọn phòng RingArray ở trên, đây là thao tác 'set' của ứng viên để khớp với thao tác phần tử 'get' ở trên:

void addElement(long entrycount,double value){ // to be called only by the keeper of entrycount
    this.entrycount= entrycount;
    cycle = (int)entrycount/modulus;
    if(cycle==0){                       // start-up is when the ring is being populated the first time around
        size = (int)entrycount;         // during start-up, size is less than modulus so use modulo size arithmetic
        tail = (int)entrycount%size;    //  during start-up
    }
    else {
        size = modulus;
        head = tail;
        tail = (int)entrycount%modulus; //  after start-up
    }
    darray[head] = value;               //  always overwrite old tail
}

-2

Tôi có thể nghĩ về một tác dụng phụ đáng tiếc. Trong cơ sở dữ liệu nhúng java, số id bạn có thể có với trường id 32 bit là 2 ^ 31, không phải 2 ^ 32 (~ 2 tỷ, không ~ 4 tỷ).


1
Có lẽ anh ta nghĩ về mảng và không thể sử dụng các số nguyên âm làm chỉ số. Có lẽ.
SK9

2
Khi các trường tăng tự động trong cơ sở dữ liệu tràn, chúng thường bị lập dị.
Joshua

-8

Lý do IMHO là vì họ đã / quá lười biếng để thực hiện / sửa lỗi đó. Đề xuất rằng các lập trình viên C / C ++ không hiểu không dấu, cấu trúc, liên kết, cờ bit ... Chỉ là vô lý.

Ether bạn đang nói chuyện với một lập trình viên cơ bản / bash / java trước khi bắt đầu lập trình a la C, không có kiến ​​thức thực sự về ngôn ngữ này hoặc bạn chỉ đang nói ra khỏi tâm trí của chính mình. ;)

Khi bạn giao dịch hàng ngày với định dạng từ tệp hoặc phần cứng, bạn bắt đầu đặt câu hỏi, họ đang nghĩ cái quái gì vậy.

Một ví dụ điển hình ở đây sẽ là cố gắng sử dụng một byte không dấu như một vòng lặp tự xoay. Đối với những người bạn không hiểu câu cuối cùng, làm thế nào trên trái đất bạn tự gọi mình là một lập trình viên.

DC


34
Chỉ để đá, Google cụm từ "vòng lặp tự xoay". Rõ ràng , Denis Co là người duy nhất trên thế giới xứng đáng tự gọi mình là lập trình viên :-)
Stephen C

6
Câu trả lời này tệ đến mức buồn cười
Nayuki
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.