Нет гарантии, что SSIZE_MAX >= SIZE_MAX
. На самом деле, это очень маловероятно, поскольку size_t
и ssize_t
, вероятно, соответствуют соответствующим типам без знака и со знаком, поэтому (на всех реальных архитектурах) SIZE_MAX > SSIZE_MAX
. Преобразование неподписанного значения в подписанный тип, который не может содержать это значение, является неопределенным поведением. Технически, ваш макрос проблематичен.
На практике, по крайней мере, на 64-битных платформах вы вряд ли столкнетесь с проблемами, если значение, которое вы конвертируете в ssize_t
, является размером реально существующего объекта. Но если объект теоретический (например, sizeof(char[3][1ULL<<62])
), вы можете получить неприятный сюрприз.
Обратите внимание, что единственным допустимым отрицательным значением типа ssize_t
является -1, что является признаком ошибки. Вы можете путать ssize_t
, который определен Posix, с ptrdiff_t
, который определен в стандарте C начиная с C99. Эти два типа одинаковы на большинстве платформ и обычно представляют собой целочисленный тип со знаком, соответствующий size_t
, но ни один из этих режимов не гарантируется ни одним из стандартов. Однако семантика двух типов различна, и вы должны знать об этом, когда используете их:
ssize_t
возвращается несколькими интерфейсами Posix, чтобы позволить функции сигнализировать либо количество обработанных байтов, либо указание ошибки; индикация ошибки должна быть -1. Не ожидается, что любой возможный размер поместится в ssize_t
; обоснование Posix гласит:
Соответствующее приложение будет ограничено, чтобы не выполнять ввод / вывод кусками, большими, чем {SSIZE_MAX}
.
Это не проблема для большинства интерфейсов, которые возвращают ssize_t
, потому что Posix обычно не требует интерфейсов для гарантии обработки всех данных. Например, read
и write
принимают size_t
, который описывает длину буфера для чтения / записи, и возвращают ssize_t
, который описывает количество байтов, фактически прочитанных / записанных; это означает, что не более SSIZE_MAX
байтов будут считываться / записываться, даже если доступно больше данных. Однако в обосновании Posix также отмечается, что конкретная реализация может обеспечить расширение, которое позволяет обрабатывать большие блоки («соответствующее приложение, использующее расширения, сможет использовать полный диапазон, если реализация предоставит расширенный диапазон»), идея заключается в том, чтобы что реализация может, например, указать, что возвращаемые значения, отличные от -1, должны интерпретироваться путем приведения их к size_t
. Такое расширение не будет переносимым; на практике большинство реализаций ограничивают количество байтов, которые могут быть обработаны за один вызов, числом, которое может быть указано в ssize_t
.
ptrdiff_t
- это (в стандарте C) тип результата разницы между двумя указателями. Чтобы вычитание указателей было правильно определено, два указателя должны ссылаться на один и тот же объект, либо указывая на объект, либо указывая на байт, следующий сразу за объектом. Комитет C признал, что если ptrdiff_t
является подписанным эквивалентом size_t
, то, возможно, разница между двумя указателями может быть не представимой, что приводит к неопределенному поведению, но они предпочли, чтобы требование ptrdiff_t
было больший тип, чем size_t
. Вы можете поспорить с этим решением - многие люди приняли - но оно действует со времен C90, и вряд ли оно изменится сейчас. (Нынешняя стандартная формулировка, & sect; 6.5.6 / 9: «Если результат не может быть представлен в объекте этого типа [ptrdiff_t
], поведение не определено.»)
Как и в Posix, стандарт C не определяет неопределенное поведение, поэтому было бы ошибкой интерпретировать это как , запрещающее вычитание двух указателей в очень больших объектах. Реализации всегда разрешено определять результат поведения, оставленного неопределенным стандартом, так что для реализации вполне допустимо указывать, что если P
и Q
являются двумя указателями на один и тот же объект, где P >= Q
, то (size_t)(P - Q)
- математически правильная разница между указателями, даже если вычитание переполняется. Конечно, код, который зависит от такого расширения, не будет полностью переносимым, но если расширение является достаточно распространенным, это не может быть проблемой.
В качестве конечной точки, неоднозначность использования -1 как в качестве индикации ошибки (в ssize_t
), так и в качестве, возможно, преобразуемого результата вычитания указателя (в ptrdiff_t
) вряд ли будет присутствовать на практике при условии что size_t
равно указателю. Если size_t
равен указателю, математически правильное значение P-Q
может быть (size_t)(-1)
(иначе SIZE_MAX
), если объект, на который ссылаются P
и Q
, имеет size SIZE_MAX
, который, исходя из предположения, что size_t
равна ширине указателя, подразумевает, что объект плюс следующий байт занимают все возможные значения указателя. Это противоречит требованию, чтобы какое-то значение указателя (NULL
) отличалось от любого допустимого адреса, поэтому мы можем заключить, что истинный максимальный размер объекта должен быть меньше SIZE_MAX
.