Безопасно ли брать разницу между двумя объектами размера t?
Я исследую стандарт для моей команды вокруг использования size_t
vs int
(или long
и т. д.). Самый большой недостаток, который я видел, заключается в том, что разность двух объектов size_t может вызвать проблемы (я не уверен в конкретных проблемах-возможно, что-то не было дополнено 2s, а подписанный/неподписанный злит компилятор). Я написал быструю программу на C++, используя компилятор V120 VS2013, который позволил мне сделать следующее:
#include <iostream>
main()
{
size_t a = 10;
size_t b = 100;
int result = a - b;
}
программа привела к -90
, что, хотя и правильно, заставляет меня нервничать по поводу несоответствий типов, проблем со знаком/без знака или просто неопределенного поведения, если size_t используется в сложной математике.
мой вопрос в том, безопасно ли делать математику с объектами size_t, в частности, принимая разницу? Я рассматриваю использование size_t в качестве стандарта для таких вещей, как индексы. Я видел несколько интересных сообщений по этой теме здесь, но они не касаются математической проблемы (или я пропустил он.)
какой тип для вычитания 2 size_t?
typedef для типа, который может содержать значение типа size_t?
3 ответов
это не гарантированно работает переносимо,но и не UB. Код должен выполняться без ошибок, но в результате int
значение определяется реализацией. Пока вы работаете на платформах, гарантирующих желаемое поведение, это нормально (если разница может быть представлена int
конечно), в противном случае, просто используйте подпись типа везде (см. Последний абзац).
вычитая два std::size_t
s даст новый std::size_t
† и его значение будет определено путем обертывания. В вашем примере, предполагая 64 бит size_t
, a - b
будет равна 18446744073709551526
. Это не вписывается в (обычно используется 32-разрядная) int
, поэтому определенное значение реализации присваивается result
.
честно говоря, я бы рекомендовал не использовать целые числа без ничего, но немного магии. Несколько членов комитета по стандартизации согласны со мной: https://channel9.msdn.com/Events/GoingNative/2013/Interactive-Panel-Ask-Us-Anything 9:50, 42:40, 1:02:50
эмпирическое правило (перефразируя Чандлера Каррута из приведенного выше видео): Если вы могли бы посчитать его самостоятельно, используйте int
, иначе -std::int64_t
.
†если его ранг преобразования не меньше int
, например,std::size_t
и unsigned short
. В этом случае, результатом является int
и все будет работать нормально (если int
не шире, чем short
). Однако
- я не знаю никакой платформы, которая сделать это.
- это все равно будет специфичной платформой, см. Первый абзац.
если вы не используете size_t
, вы облажались: size_t
- Это один тип, который существует для использования для размеров памяти и который, следовательно, всегда будет достаточно большим для этой цели. (uintptr_t
очень похоже, но это не первый такой тип, и он не используется стандартными библиотеками, и он доступен без включения stdint.h
.) Если вы используете int
, вы можете получить неопределенное поведение, когда ваши выделения превышают 2GiB адресного пространства (или 32kiB, если вы находитесь на платформе где int
только 16 бит!), даже если машина имеет больше памяти и выполняются в 64-битном режиме.
Если вам нужна разница size_t
Что может стать отрицательным, используйте подписанный вариант ssize_t
.
на size_t
тип является беззнаковым. Вычитание любых двух size_t
значения определены-поведение
однако, во-первых, результат определяется реализацией, если большее значение вычитается из меньшего размера. Результатом является математическое значение, приведенное к наименьшему положительному остатку по модулю SIZE_T_MAX + 1
. Например, если наибольшее значение size_t
равно 65535, а результат вычитания двух size_t
значений -3, тогда и результат будет 65536 - 3 = 65533. На другой компилятор или машина с другим size_t
, числовое значение будет отличаться.
во-вторых, a size_t
значение может быть вне диапазона типа int
. Если это так, мы получаем второй результат, определенный реализацией, возникающий из принудительного преобразования. В этой ситуации может применяться любое поведение; оно просто должно быть задокументировано реализацией, и преобразование не должно завершиться неудачей. Например, результат может быть зажат в