Правильно ли `-1` для использования в качестве максимального значения целого числа без знака?
Есть ли какой-либо стандартный параграф С++, в котором говорится, что использование -1
для этого является переносимым и правильным способом или единственный способ сделать это правильно - использовать предопределенные значения?
У меня был разговор с моим коллегой, что лучше: используя -1
для максимального целого числа без знака или используя значение от limits.h
или std::numeric_limits
?
Я сказал своему коллеге, что использование предопределенных максимальных значений из limits.h
или std::numeric_limits
является переносным и чистым способом сделать это, однако, коллега возражал против -1
, будучи таким же переносимым, как числовые ограничения, и больше, у него есть еще одно преимущество:
unsigned short i = -1; // unsigned short max
можно легко изменить на любой другой тип, например
unsigned long i = -1; // unsigned long max
при использовании предопределенного значения из заголовочного файла limits.h
или std::numeric_limits
также требуется переписать его вместе с типом слева.
Ответы
Ответ 1
Не использовать стандартный способ или не показывать четко намерение часто плохой идеей, что мы платим позже
Я бы предложил:
auto i = std::numeric_limits<unsigned int>::max();
или @jamesdin предложили конечно лучший, ближе к C привычки:
unsigned int i = std::numeric_limits<decltype(i)>::max();
Ваш аргумент коллеги недопустим. Изменение int
→ long int
, как показано ниже:
auto i = std::numeric_limits<unsigned long int>::max();
- не требует дополнительной работы по сравнению с решением
-1
(благодаря использованию auto
).
- "-1" решение прямо не отражает наше намерение, следовательно, оно может иметь вредные последствия. Рассмотрим этот фрагмент кода:
.
using index_t = unsigned int;
... now in another file (or far away from the previous line) ...
const index_t max_index = -1;
Во-первых, мы не понимаем, почему max_index
есть -1
.
Хуже, если кто-то хочет улучшить код и определить
using index_t = ptrdiff_t;
= > , тогда оператор max_index=-1
больше не является max
, и вы получите багги-код. Снова это не может произойти с чем-то вроде:
const index_t max_index = std::numeric_limits<index_t>::max();
CAVEAT:, однако при использовании std::numeric_limits
существует оговорка. Он не имеет ничего общего с целыми числами, но связан с числами с плавающей запятой.
std::cout << "\ndouble lowest: "
<< std::numeric_limits<double>::lowest()
<< "\ndouble min : "
<< std::numeric_limits<double>::min() << '\n';
печатает:
double lowest: -1.79769e+308
double min : 2.22507e-308 <-- maybe you expected -1.79769e+308 here!
-
min
возвращает наименьшее конечное значение данного типа
-
lowest
возвращает наименьшее конечное значение данного типа
Всегда интересно помнить, что, поскольку это может быть источником ошибки, если мы не обращаем внимания (используя min
вместо lowest
).
Ответ 2
Что касается преобразований целых чисел, C 2011 [проект N1570] 6.3.1.3 2 говорит
В противном случае, если новый тип без знака, значение преобразуется путем многократного добавления или вычитания более чем максимального значения, которое может быть представлено в новом типе, пока значение не будет в диапазоне нового типа.
Таким образом, преобразование -1 в целое число без знака обязательно порождает максимальное значение этого типа.
Могут возникнуть проблемы с использованием -1
в различных контекстах, где он не сразу преобразуется в нужный тип. Если он немедленно преобразуется в нужный целочисленный тип без знака, как при назначении или явном преобразовании, результат становится ясным. Однако, если это часть выражения, его тип int
, и он будет вести себя как int
до конвертирования. Напротив, UINT_MAX
имеет тип unsigned int
, поэтому он ведет себя как unsigned int
.
Как chux указывает в комментарии, USHRT_MAX
эффективно имеет тип int
, поэтому даже именованные ограничения не полностью безопасны от типа вопросы.
Ответ 3
Правильно ли использовать -1
в качестве максимального значения целого числа без знака?
Да, это функционально правильно, когда используется как прямое назначение/инициализация. Все же часто выглядит сомнительным @Ron.
Константы из limits.h
или std::numeric_limits
более глубокое понимание кода, но нуждаются в обслуживании в случае изменения типа i
.
[Примечание] OP позже удалите тег C.
Чтобы добавить альтернативу присвоению максимального значения (доступно в C11), которая помогает сократить обслуживание кода:
Используйте любимый/ненавистный _Generic
#define info_max(X) _Generic((X), \
long double: LDBL_MAX, \
double: DBL_MAX, \
float: FLT_MAX, \
unsigned long long: ULLONG_MAX, \
long long: LLONG_MAX, \
unsigned long: ULONG_MAX, \
long: LONG_MAX, \
unsigned: UINT_MAX, \
int: INT_MAX, \
unsigned short: USHRT_MAX, \
short: SHRT_MAX, \
unsigned char: UCHAR_MAX, \
signed char: SCHAR_MAX, \
char: CHAR_MAX, \
_Bool: 1, \
default: 1/0 \
)
int main() {
...
some_basic_type i = info_max(i);
...
}
Приведенный выше макрос info_max()
имеет ограничения, касающиеся таких типов, как size_t
, intmax_t
и т.д., intmax_t
могут не быть перечислены в приведенном выше списке. Есть более сложные макросы, которые могут справиться с этим. Идея здесь является иллюстративной.
Ответ 4
Техническая сторона была покрыта другими ответами; и в то время как вы фокусируетесь на технической правильности в своем вопросе, важно отметить, что вопрос чистоты снова важен, потому что imo - это гораздо более важный момент.
основная причина, почему это плохой идеей, чтобы использовать эту конкретную обманку: Код неоднозначен. Неясно, кто-то использовал преднамеренный трюк преднамеренно или допустил ошибку и фактически хотел инициализировать подписанную переменную до -1. Если ваш коллега упомянет комментарий после того, как вы представите этот аргумент, скажите ему, чтобы он переставал быть глупым.:)
Im фактически немного озадачил, что кто-то даже рассмотрит этот трюк всерьез. Theres однозначный, интуитивно понятный и идиоматический способ установить значение его max в C: макросы _MAX. И theres дополнительный, одинаково однозначный, интуитивно понятный и идиоматический способ в С++, который обеспечивает еще больше безопасности типов: numeric_limits. Этот трюк - классический случай умения.
Ответ 5
В стандарте С++ говорится об signed
to unsigned
преобразованиях ([conv.integral]/2):
Если тип назначения не указан, результирующее значение представляет собой наименьшее целое без знака, сравнимое с исходным целым числом (по модулю 2 n где n - количество бит, используемых для представления неподписанного типа). [Примечание: в представлении с двумя дополнениями это преобразование является концептуальным и нет изменений в битовой схеме (если нет усечения). - конечная нота]
Итак, преобразование -1 в n-разрядное целое без знака всегда даст вам 2 n -1, независимо от того, какая подпись целочисленный тип -1 начался как.
Является ли unsigned x = -1;
более или менее читаемым, чем unsigned x = UINT_MAX;
, хотя это еще одно обсуждение (определенно есть вероятность, что он поднимет некоторые брови, может быть, даже ваши собственные, когда вы посмотрите на свой собственный код позже;).