Здравствуйте, Alekzander, Вы писали:
A>Никто не считает яблоки и не задаёт порядковый номера домов в "беззнаковых целых числах", все думают о них как о "целых". Поэтому и слово unsigned (или буква u в начале) превращает мысленно проговариваемое "целое число" в некое "беззнаковое целое число".
Тут есть ещё одно. Беззнаковое целое в C|C++ — это не условный integer range(0,2**N-1), где N — количество бит (в наших реалиях обычно 32). Это integer modulo 2**N.
А название этого не отражает. Приходится постоянно держать в голове факт маппинга между терминами, и учить тех, кто этого не понимает.
A> Для меня это веская причина, т.к. код читают люди, а отвлекать их от чтения — плохая идея. Не сомневаюсь, что и Кернихан с Ричи руководствовались этим же соображением.
Во времена, когда они этим занимались, всё ещё было слишком примитивно и туманно. Нет, тут комитет постарался. Стандарт 85-го года начал стандартизовать язык, это хорошо, но они одновременно вогнали в него 100500 диверсий на будущее.
A>С другой стороны, когда авторы хотят подчеркнуть, что знака не должно быть, это тоже веская причина использовать unsigned. И веская тоже потому, что код читают люди. Но при этом, я считаю, возникает дополнительная ответственность. Надо убедиться, что моделируемое число в предметной области никогда не должно быть отрицательным (чего часто не делают). Что при расчётах промежуточные результаты не уйдут за ноль. Что для передачи ошибки предусмотрен другой канал. А если всего этого не делают, то уж лучше использовать знаковое. Которое является просто максимально общим случаем. Избыточное обобщение не ошибка, а избыточное сужение — да.
Именно.
В последних 3-4 проектах, где богатый ресурсами, но embedded, я столкнулся с эффектом, которого раньше не видел. Есть до чёрта мест, где приходит, например, количество каналов в uint8_t. Что практически все кодеры делают — это цикл вида
for (uint8_t idx = 0; idx < nchannels; ++idx)
{ ... }
То есть какого типа значение, такое выставляют и типу индекса.
Всё хорошо, но в одном месте произошло следующее. Пришло не количество каналов, а номер максимального канала. Есть те, кто любят именно такой стиль (я видел такое у Intel). Соответственно nchannels = max_channel + 1. И в реальности где дали 256 каналов цикл стал бесконечным.
Понятно, что это не проблема unsigned как такового, если знать, что он элемент поля по модулю. Это проблема качества анализа кодером (здесь это тот, кто написал код, неважно, какие ещё у него есть лычки и полномочия), что он не заметил, что именно тут переполняется возможное uint8_t. Но если бы это был честный unsigned, было бы несколько вариантов: например, статический анализатор нашёл бы проблему, или произошло бы исключение, если оно назначено для арифметики... а тут по факту любому анализатору усложнили жизнь, и никакой не пожаловался.
Смешно, но тот, кто это исправлял, первым делом загнал nchannels в переменную типа uint8_t
Пришлось ещё раз фиксить.
A>К счастью, в большинстве случаев это неважно. Работать с коллекциями лучше в функциональном стиле, а размер вообще не считать числом.
Хм. Нам вот "налили" новый проект, а там... мнэээ, разработка началась этак в 2018, но там C процентов 95, и C++ остальных 5%. (Самое смешное, что туда лучше бы подошли Go или Erlang.)
Какой уж там функциональный стиль...