@kaka888
C, C++, Qt, Python Flask, MySQL, Lua

Почему для счётчиков не используют беззнаковые (unsigned) числа?

Часто вижу в коде на C++ и C#, что для счётчиков (а для большинства счётчиков используются только натуральные числа, ну и иногда ещё ноль) используют int, а не uint/unsigned int. В итоге максимальное значение счётчика в таком коде уменьшается вдвое. Почему праграмисты делают это? Может быть это из-за плохой совместимости между int и uint? Что, по вашему мнению, правильнее выбрать?
  • Вопрос задан
  • 657 просмотров
Решения вопроса 2
vabka
@vabka
Токсичный шарпист
В целом - так исторически сложилось, что в C# используется знаковый тип для индексации массивов.
В Java вообще, например, нет беззнаковых типов. (думаю, из-за этого и в C# для индексации используется int)
В новых языках, например в Rust от этого сразу уходят - в нём для индексации массивов используется usize (беззнаковый, размером с указатель на целевой машине)
Ответ написан
Комментировать
@AlexSku
не буду отвечать из-за модератора
Смотрите фокус. Такой код выполняется:
unsigned int i = 0, N = 5;
	
	while (i < N) {
		std::cout << i << "\n";
		++i;
	}

А в таком стиле нет:
unsigned int i = 0, N = 5;
	
	while (i - N < 0) {
		std::cout << i << "\n";
		++i;
	}

Дело в том, что умный компилятор понял, что арифметическая операция с беззнаковыми числами всегда неотрицательна, и просто выкинул ваш цикл как бесполезный. Так что лучше использовать знаковые счётчики.
UPDATE
Точнее, может, он и не выкидывает, но разница это беззнаковое число, поэтому больше нуля ,поэтому первая итерация цикла не выполняется и цикл заканчивается.
Ответ написан
Пригласить эксперта
Ответы на вопрос 5
Adamos
@Adamos
У std::vector как раз не int, а size_t, который алиас для unsigned long.
В результате строгий компилятор на действия индексной арифметики, где, внезапно, вполне могут использоваться отрицательные числа, сыплет предупреждениями о том, что вместо беззнакового числа получается знаковое, ай-яй-яй. И оно с одной стороны правильно, потому что отрицательных индексов, скорее всего, не предполагается и тут могут крыться ошибки, а с другой - ну что программист с этим сделает? Приведет к беззнаковому и вместо одной ошибки спровоцирует другую?
Ответ написан
Griboks
@Griboks
Потому что длина массива имеет тип int. Счетчик, который нужен для итераций по массиву, даже теоретически не можетппреполниться, потому что всегда будет не более n-1, где n:int.

Так устроен дизайн шарпа. Но, думаю, в любом случае всем было бы лень следить за типами даже в случае индексации по unsigned.
Ответ написан
@mlyamasov
Преимущества у беззнаковых счётчиков определённо есть: https://graphitemaster.github.io/aau/
Ответ написан
Комментировать
jcmvbkbc
@jcmvbkbc
"I'm here to consult you" © Dogbert
Часто вижу в коде на C++ и C#, что для счётчиков … используют int, а не uint/unsigned int.
Почему праграмисты делают это?

Я слышал следующую теорию, почему это имеет смысл в С++: потому что значение знакового типа в корректно написанном коде не переполняется. (А если таки случилось переполнение, то это UB и всё уже не важно). Поэтому если счётчик имеет знаковый тип и с ним на каждой итерации происходит, например, инкремент, то значение счётчика только увеличивается. Это, в свою очередь, даёт дополнительные возможности для оптимизации. Беззнаковый счётчик инкрементируемый на каждой итерации может внезапно обнулиться, это может быть легальным поведением программы на которое рассчитывает программист.

А почему они фактически используют int -- наверняка потому что никакой практической разницы не видно, а буков нужно меньше.
Ответ написан
mayton2019
@mayton2019
Bigdata Engineer
По поводу Java int.

Java создавалась до 1996 года. Это было абсолютное царство 32х разрядных машин. Даже первые Pentium адресовали мало. И никто не думал о том что когда-то разрядная сетка адреса будет удвоена. Тем более что основная целевая архитектура для JVM была микроволновками, кофеварками пультами ДУ и банковскими карточками. Поэтому выбор индекса массива в 32 бита со знаком был норм. Представте что всю память 4G занимает массив целых чисел. И в этом случае достаточно максимального индекса элемента равного 1 073 741 823, больше нет смысла.

По поводу беззнаковости. На битовых операциях signed int ведет себя также как и беззнаковый. Это декларировано на уровне стандарта JVM. Лишь только два логических сдвига различают знаковый бит.

Хотя скажу честно что для меня до сих пор странно иметь компаратор который неверно работает при переполнениях. Конечно хотелось в JVM иметь семантику uint, ulong e.t.c. Есть много величин которые принципиально неотрицательные. Время. Вес. И прочие величины из физики.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы