Является ли максимальное значение размера t (SIZE MAX) определенным относительно других целочисленных типов?

я пишу библиотеку функций, которые будут безопасно конвертировать между различными числовыми типами или умереть, пытаясь. Мое намерение-примерно равные части create-useful-library и learn-c-edge-cases.

мой int - to -size_t функция вызывает GCC -Wtype-limits предупреждение, что утверждает, что я не должен проверять, если int больше SIZE_MAX, потому что это никогда не будет правдой. (Еще одна функция, которая преобразует int to ssize_t выдает идентичное предупреждение о SSIZE_MAX.)

мой MCVE, с дополнительными комментариями и детскими шагами, является:

#include <stdint.h>  /* SIZE_MAX */
#include <stdlib.h>  /* exit EXIT_FAILURE size_t */

extern size_t i2st(int value) {
    if (value < 0) {
        exit(EXIT_FAILURE);
    }
    // Safe to cast --- not too small.
    unsigned int const u_value = (unsigned int) value;
    if (u_value > SIZE_MAX) {  /* Line 10 */
        exit(EXIT_FAILURE);
    }
    // Safe to cast --- not too big.
    return (size_t) u_value;
}

Предупреждения Компилятора

я получаю аналогичные предупреждения от GCC 4.4.5 в Linux 2.6.34:

$ gcc -std=c99 -pedantic -Wall -Wextra -c -o math_utils.o math_utils.c

math_utils.c: In function ‘i2st’:
math_utils.c:10: warning: comparison is always false due to limited range of data type

...а также из GCC 4.8.5 на Linux 3.10.0:

math_utils.c: In function ‘i2st’:
math_utils.c:10:5: warning: comparison is always false due to limited range of data type [-Wtype-limits]
     if (u_value > SIZE_MAX) {  /* Line 10 */
     ^

эти предупреждения не кажутся мне оправданными, по крайней мере, в общем случае. (Я не отрицаю, что сравнение может быть - всегда ложь." на некотором определенном сочетании оборудования и компилятор.)

Стандарт C

стандарт c 1999, по-видимому, не исключает int больше SIZE_MAX.

"6.5.3.4 в sizeof оператор" не обращается size_t вообще, за исключением описания его как - определено в <stddef.h> (и другие заголовки)". "7.17 Общие определения <stddef.h>" определяет size_t as "целочисленный тип без знака результата sizeof оператора". (Благодаря, ребята!) "7.18.3 пределы других целочисленных типов" более полезно --- это определяет "предел size_t " as:

SIZE_MAX 65535

...смысл SIZE_MAX может быть маленький as 65535. Ан int (signed или unsigned) может быть намного больше, в зависимости от оборудования и компилятора.

Переполнение Стека

принятый ответ на "unsigned int и size_t" кажется, поддерживает мою интерпретацию (Курсив мой):

на size_t тип может быть больше, равен, или меньше an unsigned int и ваш компилятор может делать предположения о его оптимизации.

этот ответ приводит то же самое Раздел "7.17" из стандарта C, который я уже цитировал.

Другие Документы

мои поиски обнаружили документ открытой группы "сведения Нейтральность размера и 64-разрядная поддержка", какие претензии "64-битные модели данных " (Курсив мой):

ISO / IEC 9899: 1990, языки программирования-C (ISO C) оставил определение short int на int на long int и pointer намеренно расплывчатым [...] Единственным ограничением было то, что ints должно быть не меньше shortS и long s должно быть не меньше intS и size_t должен представлять самый большой тип без знака поддерживается реализацией. [...] Связь между основными типами данных может быть выражена следующим образом:

sizeof(char) sizeof(short) sizeof(int) sizeof(long) = sizeof(size_t)

если это правда, то испытания int против SIZE_MAX действительно бесполезно... но эта статья не цитирует главы и стихи, поэтому я не могу сказать, как ее авторы пришли к своему выводу. Собственный "Базовая Спецификация Версия 7" sys/types.h docs не обращайся к этому в любом случае.

У Меня Вопрос

я понимаю, что size_t вряд ли будет уже, чем int, а гарантирует ли стандарт C сравнение some_unsigned_int > SIZE_MAX будет всегда быть ложным? Если да, то где?

Не-Дубликатов

есть два полу-дубликата этого вопроса, но они оба задают более общие вопросы о том, что size_t предполагается представлять и когда он должен / не должен использоваться.

  • "что это size_t в C?" не затрагивает отношения между size_t и другие целочисленные типы. Его принятый ответ - это просто цитата из Википедии, которая не предоставляет никакой информации за пределами того, что я уже нашел.

  • "каково правильное определение size_t?" начинается почти дубликат моего вопрос, но затем отклоняется от курса, задавая когда size_t следует использовать и почему он был введен. Он был закрыт как дубликат предыдущего вопроса.

3 ответов


текущий стандарт C не требует size_t чтобы быть по крайней мере таким же широким, как int, а я скептически отношусь к любой версии стандарта когда-либо делал так. size_t необходимо иметь возможность представлять любое число, которое может быть размером объекта; если реализация ограничивает размеры объекта шириной 24 бита, то size_t может быть 24-битным типом без знака, независимо от того, что int есть.

предупреждение GCC не относится к теоретическим возможностям. Это проверка конкретная аппаратная платформа и конкретный компилятор и среда выполнения. Это означает, что иногда он запускает код, который пытается быть портативным. (Есть и другие случаи, когда портативный код вызовет дополнительные предупреждения GCC.) Это может быть не то, что вы надеялись сделать предупреждение, но, вероятно, есть пользователи, ожидания которых точно соответствуют реализованному поведению, и стандарт не содержит никаких рекомендаций для предупреждений компилятора.


As OP упоминает в комментарии, есть долгая история, связанная с этим предупреждением. Предупреждение было введено в версии 3.3.2 или около того (в 2003 году), по-видимому, не контролируется any -W флаг. Об этом сообщалось как ошибка 12963 пользователем, который, очевидно, чувствовал, как и вы, что предупреждение препятствует портативному программированию. Как видно из отчета об ошибке, различные сопровождающие GCC (и другие известные члены сообщества) взвешивали с сильно ощущаемыми, но противоречивыми мнениями. (Это общая динамика в отчетах об ошибках с открытым исходным кодом.) Через несколько лет было принято решение контролировать предупреждения с флагом и не включать этот флаг по умолчанию или как часть -Wall. Тем временем, была переименована в -Wextra и вновь созданного флага (-Wtype-limits) был добавлен к -Wextra коллекция. Мне кажется, это правильное решение.


остальная часть этого ответа содержит мой личный мнение.

-Wall, как описано в руководстве GCC, фактически не включает все предупреждения. Это позволяет предупреждать " о конструкциях, которые некоторые пользователи считают сомнительными, и которые легко избежать (или изменить, чтобы предотвратить предупреждение), даже в сочетании с макросами."Существует ряд других условий, которые GCC может обнаружить:

обратите внимание, что некоторые предупреждающие флаги не подразумевается -Wall. Некоторые из них предупреждают конструкции, которые пользователи обычно не считают сомнительными, но которые иногда вы можете проверить; другие предупреждают о конструкциях, которые необходимы или трудно избежать в некоторых случаях, и нет простого способа изменить код, чтобы подавить предупреждение. Некоторые из них включены с помощью -Wextra но многие из них должны быть включены по отдельности.

эти различия несколько произвольны. Например, я должен стискивать зубы каждый раз, когда GCC решает "предлагаю скобки вокруг ‘&&’ в ‘||’". (Похоже, он не чувствует необходимости предлагать круглые скобки вокруг * внутри+, что для меня не отличается.) Но я признаю, что у всех нас разные уровни комфорта с приоритетом оператора, и не все предложения GCC о скобках кажутся мне чрезмерными.

но в целом, различие кажется разумным. Существуют предупреждения, которые обычно применимы, и они включены с помощью -Wall, который должен всегда быть указанным, потому что эти предупреждения почти всегда требуют действий для исправления дефицита. Есть и другие предупреждения, которые могут быть полезны в определенных обстоятельствах, но которые также имеют много ложноположительных; эти предупреждения необходимо исследовать индивидуально, потому что они не всегда (или даже часто) соответствуют проблеме в вашем коде.

я знаю, что есть люди, которые считают, что простой факт, что GCC знает, как предупредить о каком-то условии достаточно, чтобы потребовать действий, чтобы избежать этого предупреждения. Каждый имеет право на свои стилистические и эстетические суждения, и это правильно и справедливо, что такие программисты добавляют -Wextra для их флагов сборки. Однако я не в этой толпе. В данный момент в проекте я попробую построить с большой коллекцией дополнительных предупреждений и рассмотрю, следует ли изменять мой код на основе отчетов, но я действительно не хочу тратить время на разработку, думая о не-проблемах каждый раз пора восстановить файл. The -Wtypes-limit флаг попадает в эту категорию для меня.


ничего не требует максимум size_t больше int. Такие архитектуры где SIZE_MAX is INT_MAX редки, хотя и я сомневаюсь, что GCC поддержит любой из них.

что касается исправить, вы можете использовать #if:

#if INT_MAX > SIZE_MAX
if (u_value > SIZE_MAX) {  /* Line 10 */
    exit(EXIT_FAILURE);
}
#endif

Я согласен с Ремо.Д толкование.

size_t указывается как стандартное целое число без знака, но стандарт не ограничивает его размер относительно любого из них, кроме как говоря, что он должен иметь возможность удерживать по крайней мере 65535.

он может быть меньше, равен или больше по размеру, чем unsigned int.