Безопасно ли брать разницу между двумя объектами размера 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_ts даст новый 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). Однако

  1. я не знаю никакой платформы, которая сделать это.
  2. это все равно будет специфичной платформой, см. Первый абзац.

если вы не используете 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. Если это так, мы получаем второй результат, определенный реализацией, возникающий из принудительного преобразования. В этой ситуации может применяться любое поведение; оно просто должно быть задокументировано реализацией, и преобразование не должно завершиться неудачей. Например, результат может быть зажат в