Поскольку кажется, нам следует избегать char
, int
, short
, long
или unsigned
.
«Если вы обнаружите, что набираете в новый код char, int, short, long или unsigned, значит, вы делаете это неправильно», из https://matt.sh/howto-c.
Можно ли указать компилятору предупреждать об этих случаях?
Обратите внимание, что есть раздел: Одно исключение из never-char Единственное допустимое использование char в 2016 году - это если для уже существующего API требуется char (например, strncat, printf'ing "% s", ...) или если вы инициализируете строку, доступную только для чтения. (например, const char * hello = "hello";), потому что тип строковых литералов C ("hello") - char []. ТАКЖЕ: в C11 у нас есть встроенная поддержка юникода, и тип строковых литералов UTF-8 по-прежнему char [] даже для многобайтовых последовательностей, таких как const char * abcgrr = u8 "abc?" ;. К сожалению, стандартная библиотека C написана в терминах char
и char *
- и большинство программ ее используют!
@JonathanLeffler Основная проблема заключается в том, что стандарт C предоставляет различные определения и гарантии для «встроенных» типов, в то время как для типов stdint
это не так.
Ух ты, статья в блоге о C, которая на самом деле содержит хорошие и разумные советы. Это редкость в Интернете. Не знаю, кто автор, но он знает, о чем говорит.
@EugeneSh. Нет ничего спорного в том, чтобы избегать таких типов. У них есть множество известных недостатков, они полностью непереносимы, и мы знаем это с начала 90-х годов. Именно поэтому изначально был изобретен stdint.h. C99, C11, MISRA-C и так далее, все согласны с тем, что эти типы плохие и могут существовать только ради обратной совместимости.
@Lundin Нет, не все согласны. Есть варианты использования для типов фиксированной ширины и есть варианты использования для «абстрактных» числовых типов. Когда я хочу сохранить символ, Мне все равно, сколько бит он будет потреблять. Когда я хочу выполнить простую математику с целыми числами или иметь счетчик, Мне все равно, сколько бит будет у этих целых чисел, пока у меня есть limits.h
.
@EugeneSh. Использование char
для хранения символов - единственное допустимое использование этого типа. Но если вам все равно, сколько битов в целых числах используется в арифметике, тогда вы пишете минное поле скрытых, тонких ошибок. C здесь не снисходителен и не рационален - он поразит вас переполнениями и неявным продвижением типов, если вы точно не знаете, насколько велики ваши типы и как эти типы незаметно изменяются вашим кодом. Если вы не хотите знать, не пишите код на C. Есть несколько других языков, гораздо более подходящих для тех, кто не хочет знать грязные детали.
@Lundin Во многих случаях случаем переполнения можно очень хорошо управлять, используя предельные значения типов. Я не говорю, что типы фиксированной ширины бесполезны - совсем нет. Я работаю с ними большую часть времени в разработке встраиваемых систем, но опять же. C определяется в терминах char
, int
и long
. %d
будет печатать int
, а не int32_t
. И нет, не все компиляторы имеют спецификатор формата для int32_t
. sizeof(char)
гарантированно равен 1, но не sizeof(uint8_t)
. И еще много примеров.
@EugeneSh .: Стандарт также позволяет компилятору предполагать, что uint8_t*
никогда не будет использоваться для доступа к чему-либо, кроме uint8_t
или int8_t
. Конечно, компилятор качество должен распознавать ситуации, когда uint8_t*
является производным от чего-то другого типа и используется для доступа к хранилищу, по крайней мере, до следующего обращения к хранилищу другими способами, независимо от того, требует ли это Стандарт, но это одинаково верно для любого другого типа указателя. Тем не менее, Стандарт предлагает разрешение только для реальных типов персонажей.
Заставить компилятор предупреждать об этом невозможно. Могут быть инструменты анализа кода, которые могут.
«Избегать их» в целом немного сурово. char
гарантированно является однобайтным, используется для текста даже в C11, а стандартные библиотеки часто используют зависимые от платформы типы, например int
как возвращаемое значение strcmp
.
Ссылки на инструменты анализа кода могут быть полезны.
@KcFnMi Согласен, но у меня нет личного опыта работы с такими инструментами. Не стесняйтесь предлагать все, что вам известно (отредактируйте сообщение или добавьте комментарий).
Компилятор никогда не предупредит об этом, потому что задача компилятора - проверять соответствие языку, и эти типы идеально подходят для языкового стандарта.
Если вам нужен инструмент, предупреждающий об использовании опасных типов, вы можете получить средство проверки MISRA-C. Рекомендации MISRA-C используются для создания безопасного подмножества небезопасного языка C. Среди прочего, MISRA не разрешает использование упомянутых типов, а вместо этого настаивает на использовании только типов из stdint.h
.
Многие языковые конструкции определены в терминах встроенных типов. Код, который использует абстрактные типы, отличные от символьных, должен быть написан так, чтобы он работал одинаково хорошо, если были заменены более крупные типы, и код, который полагается на абстрактные типы, превышающие минимальные размеры, указанные в Стандарте, и который любой может попытаться запустить в системах, где это не так, следует проверить, чтобы убедиться, что размеры соответствуют требованиям. Обратите внимание, что не имеет значения, действительно ли код объявляет какие-либо объекты абстрактных размеров.
Например, для int16_t x; uint16_t y;
на поведение (0xC000+x > 49152-y)
будет влиять размер int
. Если int
- 32 бита, то вычисления и сравнения будут выполняться с использованием значений со знаком. Однако, если int
16-битный, левый операнд будет оцениваться как 16-битное беззнаковое значение, а затем повышен до длинного для сравнения с правой частью, которая будет оцениваться как длинное со знаком.
Хотя было бы удобно писать код, который не зависел бы от размеров абстрактных типов, просто притворившись, что таких типов не существует, этого не добиться.
Хорошо, поскольку мой комментарий был удален из-за BS-слова, я повторю: эта статья - это слово. Предложение, обслуживающее предпосылку этого вопроса, весьма спорно.