Цели числа със знак срещу цели числа без знак за дължини/брой

За представяне на променлива за дължина или брой, по-добре ли е да използвате знакови или беззнакови цели числа?

Струва ми се, че C++ STL предпочита unsigned (std::size_t, като в std::vector::size(), вместо C# BCL обикновено предпочита подписани цели числа (като в ICollection.Count.

Като се има предвид, че дължина или брой са неотрицателни цели числа, интуицията ми ще избере без знак; но не разбирам защо дизайнерите на .NET избраха цели числа със знак.

Какъв е най-добрият подход? Какви са плюсовете и минусите на всеки от тях?


person Community    schedule 06.04.2012    source източник
comment
трябва да проверите това: stackoverflow.com/questions/3935165/   -  person IndieProgrammer    schedule 06.04.2012
comment
Подозирам, че C# използва цели числа със знак, защото целите числа без знак не са съвместими с CLS .   -  person Joe    schedule 06.04.2012
comment
@Joe Подозирате правилно. Най-вече защото Microsoft искаше да разпространи междуезичното сътрудничество, ключовата архитектурна точка на тяхната инициатива .NET, общата езикова среда за изпълнение. Въвеждането на множество разновидности на цели числа просто не беше приемливо поради проблеми със съвместимостта между езиците, така че беше декларирано като несъвместимо с CLS. Там е, получавате доста малки предупреждения, ако решите да ги използвате и искате да сте съвместими с CLS, ценен член на обществото, така да се каже. :Đ   -  person    schedule 06.04.2012


Отговори (4)


C++ използва неподписани стойности, защото се нуждаят от пълния диапазон. На 32-битова система езикът трябва да позволява да има вектор от 4 GB, а не само 2 GB. (ОС може да не ви позволи да използвате всичките 4 GB, но самият език не иска да ви пречи)

В .NET целите числа без знак не са съвместими с CLS. Можете да ги използвате (в някои .NET езици), но това ограничава преносимостта и съвместимостта. Така че за библиотеката на базовия клас те използват само цели числа със знак.

И двата случая обаче са крайни. За повечето цели подписаното int е достатъчно голямо. Така че, докато и двете предлагат диапазона, от който се нуждаете, можете да използвате и двете.

Едно предимство, което целите числа със знак понякога имат е, че те улесняват откриването на недостатъчен поток. Да предположим, че изчислявате индекс на масив и поради някакъв лош вход или може би логическа грешка във вашата програма, в крайна сметка се опитвате да получите достъп до индекс -1.

С цяло число със знак това е лесно за откриване. С unsigned ще се увие и ще стане UINT_MAX. Това прави много по-трудно откриването на грешката, защото сте очаквали положително число и сте получили положително число.

Така че наистина зависи. C++ използва unsigned, защото се нуждае от диапазона. .NET използва signed, защото трябва да работи с езици, които не имат unsigned.

В повечето случаи и двете ще работят, а понякога signed може да позволи на кода ви да открива грешки по-стабилно.

person jalf    schedule 06.04.2012
comment
Имахте предвид UINT_MAX, разбира се. Можете да сравните вашия неподписан индекс с размера на масива или INT_MAX, за да забележите препълване. По-голям или равен? Аларма! - person Alexey Frunze; 06.04.2012
comment
@Alex да, поправи това. и сигурно, но размерът на масива може да не е известен. Или може да не е проста операция за индексиране, а някаква друга функция, при която всяко положително число е потенциално валидно, но отрицателно не е. Въпросът е, че понякога има предимства и за двете - person jalf; 06.04.2012
comment
@jalf: Първо, благодаря за отличния отговор. Правилно ли разбирам - от написаното от вас - че използването на singed цели числа е по-безопасно от използването на unsigned? Ако е така, може би BCL използва цели числа със знак поради тази причина освен CLS ограниченията? - person ; 06.04.2012
comment
@Mr_C64: зависи от контекста. Просто посочих един сценарий, при който може да е по-безопасно. Алекс показа друг, където няма значение (и където проверката на грешки всъщност става по-проста с unsigned int). Ще трябва да помислите за това сами, за конкретната ситуация, в която вие имате нужда от това. :) - person jalf; 06.04.2012
comment
Вярно е, но използването на unsigned за покриване на пълния възможен диапазон наистина има смисъл в 16-битовите системи, не толкова в 32-битовите. Веднага щом се доближите до милиарди елементи, можете също да използвате 64-битови типове. Наличието на неподписан в ежедневния код най-вече привлича грешки, предупреждения, бъркотия и болка. - person Jem; 06.12.2012
comment
Диапазонът от стойности без знак е незначителен проблем в сравнение с факта, че почти всички аритметични операции с числа без знак дават дефинирани резултати, с единствените изключения на делене с нула, преоразмерени смени и умножение на числа без знак, чийто тип е по-малък от int, но чийто продукт няма да се побере в int [напр. на машина с 64-битов int, (uint32_t)3037000500 * (uint32_t)3037000500]. - person supercat; 04.03.2014
comment
IMO беше грешка при използването на unsigned int за размери на контейнери. Трябваше да е обикновен вътрешен. Сигурен съм, че толкова много хора са имали толкова много грешки, защото са започнали да използват size_t в кода, за да избегнат предупрежденията на компилатора. - person Pavel P; 06.10.2019

Естествено е да използваме неподписани типове за преброяване и размери, освен ако не сме в някакъв контекст, където те могат да бъдат отрицателни и въпреки това да имат смисъл. Предполагам, че C++ следва същата логика на своя по-голям брат C, в който strlen() връща size_t, а malloc() взема size_t.

Проблемът в C++ (и C) с целите числа със знак и без знак е, че трябва да знаете как се преобразуват едно в друго, когато използвате смес от двата вида. Някои препоръчват използването на int със знак за всичко цяло число, за да се избегне този проблем с невежеството и невниманието на програмистите. Но мисля, че програмистите трябва да знаят как да използват своите търговски инструменти (програмни езици, компилатори и т.н.). Рано или късно ще бъдат захапани от преобразуването, ако не в това, което са написали, то в това, което някой друг има. Това е неизбежно.

Така че познавайте инструментите си, изберете какво има смисъл във вашата ситуация.

person Alexey Frunze    schedule 06.04.2012

Тук има няколко аспекта:

1) Максимални стойности: обикновено максималната стойност на число със знак е 1/2 от съответната максимална стойност без знак. Например в C максималната кратка стойност със знак е 32767, докато максималната къса стойност без знак е 65535 (защото 1/2 от диапазона не е необходим за числата -ve). Така че, ако очаквате дължини или брои, които ще бъдат големи, представянето без знак има повече смисъл.

2) Сигурност: Можете да сърфирате в мрежата за грешки при препълване на цели числа, но си представете код като:

if (length <= 100)
{
  // do something with file
}

... тогава, ако 'length' е стойност със знак, вие рискувате 'length' да е -ve число (макар и злонамерено намерение, някакво предаване и т.н.) и кодът да не изпълни очакваното от вас. Виждал съм това в предишен проект, където последователност беше увеличена за всяка транзакция, но когато използваното от нас цяло число със знак достигна максимална стойност със знак (2147483647), то внезапно стана -ve след следващото увеличение и кодът ни не можа да се справи то.

Само някои неща, за които да помислите, независимо от съображенията за основния език/API.

person Gary Robinson    schedule 06.04.2012
comment
Друг проблем може да бъде код като while (--size >= 0) ... Когато size е без знак, условието винаги е вярно. - person ; 06.04.2012
comment
От друга страна, while(size-- > 0) е надежден идиом (въпреки че най-вече в C/C++, не толкова C#, тъй като се набляга толкова много на използването на подписани типове навсякъде, че използването на неподписани типове е повече проблем, отколкото си струва, тъй като трябва да прехвърляте по същество през цялото време). Подписаните типове няма да спасят лошия код, а само ще скрият логически грешки :) - person Thomas; 14.11.2014
comment
съответстващи --› Въпреки че size_t и ssize_t имат подобни имена, техните POSIX дефиниции не съответстват. ssize_t може да е със същата битова ширина като size_t, може да е по-широк. ssize_t Използва се за преброяване на байтове или индикация за грешка казва нищо за size_t. - person chux - Reinstate Monica; 04.01.2020

Ако не проектирате библиотека за многократна употреба (в термините на .NET, например VB.NET проект консумира вашата C# библиотека от класове), тогава изберете това, което работи за вас. Разбира се, ако създавате някакъв вид DLL и е възможно вашата библиотека да се използва в проект с различен език (отново, VB.NET идва на ум), тогава трябва да имате предвид несъответстващите типове (неподписани ).

person Chris    schedule 06.04.2012