Я работал над несколькими различными встроенными системами. Все они использовали typedef
s (или #defines
) для таких типов, как UINT32
.
Это хорошая методика, так как она сообщает программисту размер шрифта и дает вам больше шансов на переполнение и т. Д.
Но в некоторых системах вы знаете, что компилятор и процессор не будут меняться на протяжении всего проекта.
Итак, что должно повлиять на ваше решение о создании и реализации конкретных типов проектов?
РЕДАКТИРОВАТЬ Я думаю, что мне удалось потерять суть моего вопроса, и, возможно, это действительно два.
При встроенном программировании вам могут потребоваться типы определенного размера для интерфейсов, а также для работы с ограниченными ресурсами, такими как ОЗУ. Этого нельзя избежать, но вы можете использовать базовые типы из компилятора.
Для всего остального типы имеют меньшее значение.
Вы должны быть осторожны, чтобы не вызвать переполнение и, возможно, должны следить за использованием регистров и стеков. Который может привести вас к UINT16
, UCHAR
. Однако использование таких типов, как UCHAR
может добавить компилятор 'fluff'. Поскольку регистры обычно больше, некоторые компиляторы могут добавлять код, чтобы принудительно преобразовать результат в тип.
я ++;может стать
ДОБАВИТЬ РЕГ, 1 AND REG, 0xFFчто не нужно.
Поэтому я думаю, что мой вопрос должен был быть:
учитывая ограничения встроенного программного обеспечения, какую политику лучше всего установить для проекта, над которым будут работать многие люди - не все из них будут иметь одинаковый опыт.
Я использую абстракцию типов очень редко. Вот мои аргументы, отсортированные в порядке возрастания субъективности:
Локальные переменные отличаются от членов структуры и массивов в том смысле, что вы хотите, чтобы они помещались в регистр. На цели 32b / 64b локальный
int16_t
может сделать код медленнее по сравнению с локальным int, так как компилятору придется добавлять операции в / force / overflow в соответствии с семантикойint16_t
. В то время как C99 определяетintfast_t
typedef, AFAIK обычное int будет вписываться в регистр так же хорошо, и это, конечно, более короткое имя.Организации, которым нравятся эти определения типов, почти всегда заканчивают несколькими из них (
INT32, int32_t, INT32_T
до бесконечности). Таким образом, организациям, использующим встроенные типы, лучше иметь только один набор имен. Я хотел бы, чтобы люди использовали typedefs из stdint.h или windows.h или чего-либо существующего; и когда у цели нет этого .h файла, насколько сложно его добавить?Определения типов теоретически могут помочь переносимости, но я, например, никогда ничего от них не получил. Есть ли полезная система, которую вы можете портировать с цели 32b на 16b? Существует ли система 16b, которая не является тривиальной для портирования на цель 32b? Более того, если большинство переменных являются целочисленными, вы на самом деле получите что-то от 32 битов на новой цели, но если они
int16_t
вы не будете. А места, которые трудно переносить, все равно требуют ручного осмотра; прежде чем попробовать порт, вы не знаете, где они находятся. Теперь, если кто-то думает, что так легко портировать вещи, если у вас повсюду есть typedefs - когда приходит время к порту, что случается с несколькими системами, напишите скрипт, преобразующий все имена в базе кода. Это должно работать в соответствии с логикой «нет необходимости в ручной проверке» и откладывает усилия до момента, когда это действительно приносит пользу.Теперь, если переносимость может быть теоретическим преимуществом typedefs, читабельность наверняка пойдет на спад. Просто посмотрите на stdint.h:
{int,uint}{max,fast,least}{8,16,32,64}_t
. Много типов. Программа имеет много переменных; действительно ли так легко понять, что должно быть,int_fast16_t
а что должно бытьuint_least32_t
? Сколько раз мы молча обращаемся между ними, делая их совершенно бессмысленными? (Мне особенно нравятся преобразования BOOL / Bool / eBool / boolean / bool / int. Каждая программа, написанная упорядоченной организацией, предписывающей typedefs, усеяна этим).Конечно, в C ++ мы могли бы сделать систему типов более строгой, заключив числа в экземпляры класса шаблона в перегруженные операторы и прочее. Это означает, что теперь вы получите сообщения об ошибках вида «класс Number <int, Least, 32> не имеет оператора + перегрузка для аргумента типа класса Number <unsigned long long, Fast, 64>, кандидаты ...» I не называйте это "читабельностью". Ваши шансы на правильную реализацию этих классов-оберток невелики, и большую часть времени вы будете ждать, пока не скомпилируются бесчисленные экземпляры шаблонов.
Согласованность, удобство и удобочитаемость. «UINT32» гораздо более читабелен и доступен для записи, чем «unsigned long long», что является эквивалентом для некоторых систем.
Кроме того, компилятор и процессор могут быть исправлены на время жизни проекта, но код из этого проекта может найти новую жизнь в другом проекте. В этом случае наличие согласованных типов данных очень удобно.
Стандарт C99 имеет ряд стандартных размерных целочисленных типов. Если вы можете использовать компилятор, который поддерживает C99 (gcc поддерживает), вы найдете их в <stdint.h>
и можете просто использовать их в своих проектах.
Кроме того, во встроенных проектах может быть особенно важно использовать типы как своего рода «сеть безопасности» для таких вещей, как преобразование единиц. Если вы можете использовать C ++, я понимаю, что есть некоторые «модульные» библиотеки, которые позволяют вам работать в физических единицах, определенных системой типов C ++ (с помощью шаблонов), которые скомпилированы как операции с базовыми скалярными типами. Например, эти библиотеки не позволят вам добавить a distance_t
к a, mass_t
потому что модули не выстраиваются в очередь; вы на самом деле получите ошибку компилятора.
Даже если вы не можете работать на C ++ или другом языке, который позволяет вам писать код таким образом, вы можете, по крайней мере, использовать систему типов C, чтобы помочь вам ловить подобные ошибки на глаз. (На самом деле это было первоначальное намерение венгерской нотации Симони.) То, что компилятор не будет кричать на вас за добавление a meter_t
в a gram_t
, не означает, что вы не должны использовать такие типы. Обзоры кода будут гораздо более продуктивными при обнаружении ошибок модуля.
Мое мнение таково: если вы зависите от минимального / максимального / определенного размера , не просто предполагайте, что (скажем) an unsigned int
составляет 32 байта - используйте uint32_t
вместо этого (при условии, что ваш компилятор поддерживает C99).
Мне нравится использовать типы stdint.h для определения системных API, в частности, потому что они явно говорят о том, каковы большие элементы. В прежние времена в Palm OS системные API-интерфейсы определялись с использованием множества «лишенных смысла» типов, таких как «Word» и «SWord», которые были унаследованы от очень классической Mac OS. Вместо этого они произвели очистку, чтобы сказать Int16, и это облегчило понимание API для новичков, особенно с помощью странных проблем с 16-битными указателями в этой системе. Когда они разрабатывали Palm OS Cobalt, они снова изменили эти имена в соответствии с именами stdint.h, что сделало его еще более понятным и уменьшило количество typedef, которыми они должны были управлять.
stdint.h
. Лучший способ пойти на переносимость. Если у платформы его нет, его просто создать. Я считаю, что стандарты MISRA предполагают (требуют?) Использование typedefs.
С личной точки зрения, использование typedefs не оставляет путаницы относительно размера (в битах / байтах) определенных типов. Я видел, как ведущие разработчики пытались использовать оба способа разработки с использованием стандартных типов, например int, и пользовательских типов, например UINT32.
Если код не является переносимым мало реальная выгода в использовании определений типов, однако , если , как я тогда работать на обоих типах программного обеспечения (портативные и стационарные среды) , то это может быть полезно , чтобы сохранить стандарт и использовать cutomised типы. Как минимум, как вы говорите, программист очень хорошо знает, сколько памяти они используют. Другой фактор, который следует учитывать, - насколько вы уверены, что код не будет перенесен в другую среду? Я видел, что код, специфичный для процессора, должен быть переведен, поскольку аппаратному инженеру внезапно пришлось сменить плату, это не очень хорошая ситуация, но из-за пользовательских typedefs это могло быть намного хуже!
Может быть, я странный, но я использую ub, ui, ul, sb, si и sl для целочисленных типов. Возможно, «i» для 16 бит выглядит немного устаревшим, но мне больше нравится внешний вид ui / si, чем uw / sw.
Если ваши встроенные системы каким-то образом являются критически важной для безопасности системой (или аналогичной), настоятельно рекомендуется (если не требуется) использовать typedef для простых типов.
Как ТЗ. Как уже было сказано, MISRA-C имеет (консультативное) правило для этого:
Правило 6.3 (рекомендация): вместо основных числовых типов следует использовать typedef, которые указывают размер и подпись.
(из MISRA-C 2004; это правило № 13 (adv) от MISRA-C 1998)
То же самое относится и к C ++ в этой области; например. Стандарты кодирования JSF C ++ :
AV Rule 209 Будет создан файл UniversalTypes, чтобы определить все стандартные типы для использования разработчиками. Типы включают: [uint16, int16, uint32_t и т. Д.]
Использование <stdint.h>
делает ваш код более переносимым для модульного тестирования на ПК.
Он может сильно укусить вас, когда у вас есть тесты для всего, но он все еще ломается в вашей целевой системе, потому что его int
длина составляет всего 16 бит.
uint_fast8_t
fromstdint.h
. Он определяется как быстрый тип, который может поддерживать 8-разрядное значение без знака. На одной платформе это может бытьunsigned char
. По другому это может быть простоunsigned int
.