używanie uint vs int [zamknięte]


83

Zauważyłem przez jakiś czas, że programiści C # mają tendencję do używania int wszędzie i rzadko uciekają się do uint. Ale nigdy nie znalazłem satysfakcjonującej odpowiedzi, dlaczego.

Jeśli Twoim celem jest interoperacyjność, uint nie powinien pojawiać się w publicznych interfejsach API, ponieważ nie wszystkie języki interfejsu wiersza polecenia obsługują liczby całkowite bez znaku. Ale to nie wyjaśnia, dlaczego int jest tak powszechne, nawet na klasach wewnętrznych. Podejrzewam, że jest to powód, dla którego uint jest używany oszczędnie w BCL.

W C ++, jeśli masz liczbę całkowitą, dla której wartości ujemne nie mają sensu, wybierz liczbę całkowitą bez znaku.

To wyraźnie oznacza, że ​​liczby ujemne są niedozwolone lub oczekiwane, a kompilator sprawdzi za Ciebie. Podejrzewam też, że w przypadku indeksów tablicowych, JIT może łatwo zrzucić sprawdzanie dolnej granicy.

Jednak podczas mieszania typów int i jednostek konieczna będzie dodatkowa ostrożność i odlewy.

Czy należy używać go częściej? Czemu?



Właśnie pomyślałem, że miałem deja vu: D, prawie dokładnie to samo pytanie zostało zadane niedawno.
KroaX

Jeśli chodzi o niższych granic kontroli (w przypadku trzeba pisać własne) można zastąpić if (i < 0 || i >= length)z if (unchecked((uint)i) >= length). Wynikowy IL będzie miał jedną (rozgałęzioną) instrukcję mniej w sumie i da w przybliżeniu taką samą wydajność (nieskończenie szybciej). Osobiście uwielbiam to po prostu dlatego, że drapie mnie przed sprawdzaniem dolnych granic. Inni prawdopodobnie będą argumentować przeciwko temu z powodu „niezaznaczenia”, ale ja twierdzę, że jest to bardzo dobra linia do poznania jej znaczenia, ponieważ jest prosta i natychmiastowo jasno wynika z kontekstu, jaki jest cel = pomaga czytelnikowi w nauce.
AnorZaken,

Zapomniałem wspomnieć, że powyższe jest optymalne w kompilacjach 64-bitowych, ponieważ przeprowadzi porównanie z 64-bitowymi. W przypadku wersji 32-bitowych if (unchecked((uint)i) >= unchecked((uint)length))zapewnia lepszą wydajność. Wygląda to jednak na bardzo zawiłe, a 64-bitowe porównanie jest nadal bardziej wydajne niż standardowe sprawdzanie granic podwójnego rozgałęzienia w kompilacji 32-bitowej, więc nie mogę tego polecić w żadnej rozsądnej sytuacji. (Wspominam o tym głównie, aby zwrócić uwagę, że w innym przypadku używane jest porównanie 64-bitowe - co może być przydatne dla niektórych).
AnorZaken,

Panowie, twierdzę, że wasza podstawowa opinia nie jest dobra. Jeśli jest możliwa obiektywna odpowiedź, na przykład chciałbym ją usłyszeć. Jestem za zmianą moich praktyk na korzyść.
Joshua

Odpowiedzi:


50

uintPodejrzewam, że głównym powodem jest Twoja obserwacja, dlaczego nie jest używana w BCL.

UInt32 nie jest zgodny z CLS, co oznacza, że ​​jest całkowicie nieodpowiedni do użytku w publicznych interfejsach API. Jeśli zamierzasz używać uint w swoim prywatnym interfejsie API, będzie to oznaczać wykonywanie konwersji na inne typy - i zazwyczaj łatwiej i bezpieczniej jest po prostu zachować ten sam typ.

Podejrzewam również, że nie jest to tak powszechne w programowaniu w C #, nawet jeśli C # jest jedynym używanym językiem, głównie dlatego, że nie jest powszechny w BCL. Deweloperzy, ogólnie rzecz biorąc, próbują (na szczęście) naśladować styl frameworka, na którym budują - w przypadku C # oznacza to próbę, aby twoje API, publiczne i wewnętrzne, wyglądały jak najbardziej podobne do .NET Framework BCL. Oznaczałoby to oszczędne używanie uint.


1
stackoverflow.com/questions/2013116/… to pytanie, które dotyczy podobnego tematu
Stephan

77

intjest krótszy do wpisania niż uint.


2
Podejrzewam, że jest to dość bliskie prawdy. Po co używać, uintskoro intwystarczy 99% czasu (z mojego doświadczenia) ?
Matthew Jones

12
@Justin: „Magiczne liczby”, takie jak -1, nie są ogólnie dobrym pomysłem. Przełączenie na długie oznacza również użycie 2x pamięci bez powodu, a także… „jednostka” jest zdecydowanie cenna, pod warunkiem, że nie ma potrzeby interakcji z innymi interfejsami API.
Reed Copsey

28
Nigdy nie czuję się komfortowo, używając intdo indeksowania tablicy, ponieważ nigdy nie będę miał ujemnego indeksu. Wydaje się ślepo oczywiste, że uintw tym przypadku należy użyć litery a.
Mark H

2
Nie wspominając, bardziej czytelne. Jeśli kiedykolwiek przekażesz swój kod / algorytmy komuś do przeczytania, może to być mniej doświadczone niż ty, użycie wielu uintmoże je trochę zawiesić. intjest całkowicie akceptowalny we wszystkich sytuacjach, w których kontrolujesz zakres, jaki zajmie.
drharris

3
@MarkH całkowicie zgodził się, ale kiedy robi odwrotnego iteracji może być przydatna w postaci: for (int i = arr.Length - 1; i >= 0; i--) { }. Zrobienie tego z uint spowoduje wyjątek przepełnienia lub, co gorsza, nieskończoną pętlę.
Aidiakapi

19

Zwykle intwystarczy. Jeśli możesz spełnić wszystkie poniższe warunki, możesz użyć uint:

  • Nie jest przeznaczony dla publicznego interfejsu API (ponieważ uintnie jest zgodny z CLS).
  • Nie potrzebujesz liczb ujemnych.
  • Możesz potrzebować dodatkowego zakresu.
  • Jesteś nie używając go w porównaniu z < 0, jako że nigdy nie jest true.
  • Jesteś nie używając go w porównaniu z >= 0, jako że nigdy nie jest false.

Ostatni wymóg jest często zapomniany i wprowadzi błędy:

static void Main(string[] args)
{
    if (args.Length == 0) return;
    uint last = (uint)(args.Length - 1);

    // This will eventually throw an IndexOutOfRangeException:
    for (uint i = last; i >= 0; i--)
    {
        Console.WriteLine(args[i]);
    }
}

13

1) Zły nawyk. Poważnie. Nawet w C / C ++.

Pomyśl o wspólnym forwzorcu:

for( int i=0; i<3; i++ )
    foo(i);

Nie ma absolutnie żadnego powodu, aby używać tam liczby całkowitej. Nigdy nie będziesz mieć wartości ujemnych. Ale prawie każdy zrobi w ten sposób prostą pętlę, nawet jeśli zawiera ona (co najmniej) dwa inne „stylowe” błędy.

2) intjest postrzegany jako rodzimy typ maszyny.


4

Wolę uint, aby into ile liczba ujemna jest rzeczywiście w zakresie dopuszczalnych wartości. W szczególności akceptowanie intparametru, ale rzucanie znaku, ArgumentExceptionjeśli liczba jest mniejsza od zera, jest po prostu głupie - użyj uint!

Zgadzam się, że uintjest to niedostatecznie wykorzystywane i zachęcam wszystkich do częstszego korzystania z niego.


7
Bardzo niebezpieczne jest przyjmowanie tylko monet i nie sprawdzanie granic. Jeśli ktoś przekaże wartość ujemną, środowisko CLR zinterpretuje ją jako dużą liczbę int, co oznacza, że ​​dla -1 otrzymasz uint.maxvalue. Nie jest to pożądane zachowanie.
Henri

19
@Henri: C # nie ma niejawnej konwersji z int na uint, więc nie ma „Jeśli ktoś przekaże wartość ujemną”. Oczywiście sprawdzenie granic górnej granicy jest nadal właściwe (ale teraz potrzebujesz tylko jednego czeku zamiast dwóch).
Ben Voigt

1

Programuję na niższym poziomie warstwy aplikacji, gdzie wartości ints rzadko przekraczają 100, więc wartości ujemne nie są problemem (np. Dla rzeczy typu i <myname.length ()) to tylko stary nawyk języka C - i krótszy do wpisania, jak wspomniano powyżej. Jednak w niektórych przypadkach, podczas łączenia się ze sprzętem, gdzie mam do czynienia z flagami zdarzeń z urządzeń, uint jest ważny w przypadkach, gdy flaga może używać lewego (najwyższego) bitu.

Szczerze mówiąc, przez 99,9% mojej pracy mógłbym z łatwością wykorzystać ushort, ale int, wiesz, brzmi o wiele lepiej niż ushort.


1

Zrobiłem opakowanie Direct3D 10 w C # i muszę użyć uint, jeśli chcę utworzyć bardzo duże bufory wierzchołków. Dużych buforów na karcie graficznej nie można przedstawić za pomocą podpisanego int.

UINT jest bardzo przydatne i głupio jest mówić inaczej. Jeśli ktoś myśli tylko dlatego, że nigdy nie musiał go używać, nikt inny tego nie zrobi, to się mylisz.


To dobry przypadek, w którym możesz skorzystać z szerszego zakresu.
Leo Gurdian

0

Myślę, że to po prostu lenistwo. C # jest z natury wyborem do programowania na komputerach stacjonarnych i innych maszynach ze stosunkowo dużą ilością zasobów.

Jednak C i C ++ mają głębokie korzenie w starych systemach i systemach wbudowanych, w których pamięć jest rzadka, więc programiści są przyzwyczajeni do dokładnego przemyślenia, jakiego typu danych użyć. Programiści C # są leniwi, a ponieważ ogólnie jest wystarczająco dużo zasobów, nikt tak naprawdę nie optymalizuje użycia pamięci (ogólnie rzecz biorąc, nie zawsze oczywiście). Jeśli bajt byłby wystarczający, wielu programistów C #, w tym ja, po prostu używa int dla uproszczenia. Co więcej, wiele funkcji API akceptuje wartości int, więc zapobiega to rzutowaniu.

Zgadzam się, że wybranie odpowiedniego typu danych jest dobrą praktyką, ale myślę, że główną motywacją jest lenistwo.

Wreszcie wybranie liczby całkowitej jest bardziej poprawne matematycznie. Liczba całkowita bez znaku nie istnieje w matematyce (tylko liczby naturalne). A ponieważ większość programistów ma podstawy matematyczne, użycie liczb całkowitych jest bardziej naturalne.


Nie powiedziałbym, że to lenistwo, chociaż lenistwo ma swoje zalety. Co więcej, przez większość czasu po prostu nie dbam wystarczająco o int / uint, marnować cykle mózgowe na taką decyzję i po prostu iść z int. Sprzęt jest tani, programiści mogą być kosztowni.
SWeko

Programiści są leniwi. To zła rzecz. Raymond powiedziałby, że programiści nienawidzą płacić podatków!
lornova

Byłbym pierwszym, który administruje, że my, programiści C #, jesteśmy leniwi, ale to niekoniecznie jest złe.
ChaosPandion

@Lorenzo, napisałem artykuł na uniwersytecie, w którym stwierdziłem, że leniwy programista to dobry programista. Przede wszystkim chodziło o optymalizację pod kątem czasu programisty zamiast czasu maszynowego.
Eloff

1
Hmm, większość błędów programistów, które kiedykolwiek widziałem (lub zrobiłem), jest spowodowanych lenistwem ...
lornova

0

Myślę, że w dużej mierze powodem jest to, że kiedy C po raz pierwszy wyszedł, większość przykładów użytych intdla zwięzłości. Cieszyliśmy się, integerże nie musieliśmy pisać tak, jak robiliśmy to z Fortranem i Pascalem, aw tamtych czasach rutynowo używaliśmy ich do przyziemnych rzeczy, takich jak indeksy tablic i liczniki pętli. Liczby całkowite bez znaku były specjalnymi przypadkami dla dużych liczb, które wymagały tego ostatniego dodatkowego bitu. Myślę, że to naturalny postęp, że nawyki języka C były kontynuowane w C # i innych nowych językach, takich jak Python.


0

Niektóre języki (np. Wiele wersji Pascala) traktują typy bez znaku jako reprezentujące wielkości numeryczne; operacja między typem bez znaku a typem ze znakiem o tym samym rozmiarze będzie generalnie wykonywana tak, jakby operandy były promowane do następnego większego typu (w niektórych takich językach największy typ nie ma odpowiednika bez znaku, więc taka promocja zawsze będzie możliwa ).

Inne języki (np. C) traktują N-bitowe typy bez znaku jako grupę otaczającą modulo 2 ^ N. Zauważ, że odejmowanie N od członka takiej grupy nie reprezentuje odejmowania liczbowego, ale raczej daje element grupy, który po dodaniu N dałby oryginał. Prawdopodobnie niektóre operacje obejmujące mieszanie wartości ze znakiem i bez znaku nie mają tak naprawdę sensu i być może powinny być zabronione, ale nawet kod, który jest niechlujny ze specyfikacjami rzeczy, takich jak literały numeryczne, zwykle zadziała, a kod został napisany, który łączy podpis i niepodpisane typy i pomimo tego, że są niechlujne, działają, że specyfikacja nie zmieni się w najbliższym czasie.

Dużo łatwiej jest pracować wyłącznie z typami podpisanymi niż wypracować wszystkie zawiłości interakcji między typami podpisanymi i niepodpisanymi. Typy bez znaku są przydatne przy rozkładaniu dużych liczb na mniejsze części (np. W celu serializacji) lub przy odtwarzaniu takich liczb, ale ogólnie lepiej jest po prostu używać liczb ze znakiem dla rzeczy, które faktycznie reprezentują ilości


0

Wiem, że to prawdopodobnie stary wątek, ale chciałem wyjaśnić.

Weźmy int8, w którym można zapisać od –128 do 127 i używa 1 bajtu, czyli łącznie 127 liczb dodatnich.
Kiedy używasz int8, jeden z bitów jest używany na liczby ujemne -128.
Kiedy używasz Uint8, dodajesz liczby ujemne do dodatnich, więc pozwala to na użycie 255 liczb dodatnich z taką samą ilością pamięci 1 bajt.
Jedyną wadą jest utrata możliwości używania wartości ujemnych.
Innym problemem jest to, że nie wszystkie języki programowania i bazy danych to obsługują.
Moim zdaniem jedyny powód, dla którego byś tego użył, to sytuacja, gdy musisz być skuteczny w programowaniu gier i musisz przechowywać duże liczby nieujemne. Dlatego niewiele programów go używa.

Głównym powodem jest to, że przechowywanie nie stanowi problemu i nie można go elastycznie używać z innym oprogramowaniem, wtyczkami, bazą danych lub interfejsami API. Na przykład bank potrzebowałby liczb ujemnych do przechowywania pieniędzy itp.

Mam nadzieję, że to komuś pomoże.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.