Почему программисты на C используют typedef для переименования базовых типов?
Я не эксперт в C, но меня давно беспокоит один вопрос по поводу кода, который я читаю: можете ли вы объяснить, почему программисты на C (и C++) используют typedef для переименования простых типов? Я понимаю, зачем это нужно для структур, но в чем именно причина таких объявлений, как:
typedef unsigned char uch;
typedef uch UBYTE;
typedef unsigned long ulg;
typedef unsigned int u32;
typedef signed short s16;
Есть ли какие-то преимущества в этом, которые мне не до конца ясны (учитывая, что мой опыт начинается с Java и я не сильно углублялся в языки с строгой типизацией)? Я не могу подумать ни о какой причине для этого — кажется, что это только усложняет чтение кода для людей, незнакомых с проектом.
Не стесняйтесь считать меня новичком в C, я действительно знаю о нем очень мало и, вероятно, есть вещи, которые я неправильно понимаю с самого начала. 😉
5 ответ(ов)
Переименование типов без изменения их семантики или характеристик не имеет большого смысла. В вашем примере
typedef unsigned char uch;
typedef unsigned long ulg;
это именно та категория. Я не вижу в этом смысла, кроме как сделать имена короче.
Но в случае этих определений
typedef uch UBYTE;
typedef unsigned int u32;
typedef signed short s16;
это совершенно иная история. Например, s16
обозначает "знаковый тип 16 бит". Этот тип не обязательно должен быть signed short
. Конкретный тип, скрывающийся за s16
, зависит от платформы. Программисты вводят этот дополнительный уровень именования, чтобы упростить поддержку нескольких платформ. Если на какой-то другой платформе знаковый 16-битный тип оказывается signed int
, программисту нужно будет изменить лишь одно определение типа. UBYTE
, очевидно, обозначает "беззнаковый байт машины", который не обязательно соответствует unsigned char
.
Стоит отметить, что стандарт C99 уже предоставляет стандартную номенклатуру для целых типов определенной ширины, такие как int16_t
, uint32_t
и так далее. Возможно, имеет смысл придерживаться этого стандартного соглашения об именах на платформах, которые не поддерживают C99.
Это обеспечивает переносимость. Например, вам нужен беззнаковый 32-битный целочисленный тип. Какой стандартный тип для этого? Вы не знаете — это определяется реализацией. Вот почему вы используете typedef
, чтобы создать отдельный тип для 32-битного беззнакового целого числа и используете этот новый тип в своем коде. Когда вам нужно скомпилировать код на другой реализации C, вы просто изменяете typedef
s.
Иногда typedef используется для упрощения сложных типов, таких как volatile unsigned long
, превращая их в более компактные обозначения, например, vuint32_t
.
В других случаях это помогает обеспечить переносимость кода, поскольку такие типы, как int
, не всегда одинаковы на разных платформах. Используя typedef, вы можете связать интересующий вас класс хранения с ближайшим соответствием на конкретной платформе, не изменяя весь исходный код.
Есть несколько причин для этого. На мой взгляд, можно выделить следующие:
- Имя типа становится короче, что приводит к уменьшению объема кода и повышению его читаемости.
- Эффект псевдонима для более длинных названий структур.
- Конвенции, принятые в конкретной команде/компании/стиле программирования.
- Портируемость - использование одинаковых имен на всех операционных системах и на разных машинах. Его нативная структура данных может немного отличаться.
Ответ на ваш вопрос можно сформулировать следующим образом:
Далее приведена цитата из книги "Язык программирования C" (K&R):
Кроме сугубо эстетических соображений, есть две основные причины для использования
typedef
.
Первая причина — параметризовать программу
Первая причина заключается в параметризации программы в отношении проблем портируемости. Если
typedef
используются для типов данных, которые могут зависеть от конкретной машины, то толькоtypedef
нужно будет изменить при переносе программы.Одна распространенная ситуация — использовать имена
typedef
для различных целочисленных величин, а затем сделать соответствующий набор выборов типа short, int и long для каждой конкретной машины. Типы, такие какsize_t
иptrdiff_t
из стандартной библиотеки, являются примерами.
Курсивом выделенные части указывают на то, что программисты используют typedef
для базовых типов данных, чтобы обеспечить портативность. Если я хочу убедиться, что моя программа работает на различных платформах и с разными компиляторами, я постараюсь обеспечить её портируемость всеми возможными способами, и typedef
является одним из них.
Когда я начал программировать, используя компилятор Turbo C на платформе Windows, размер int
составил 2. Когда я перешел на платформу Linux и компилятор GCC, размер, который я получил, оказался 4. Если бы я разработал программу с использованием Turbo C, которая полагалась на утверждение, что sizeof(int)
всегда равен двум, она бы не перенеслась корректно на мою новую платформу.
Надеюсь, это поможет.
Следующая цитата из K&R не относится напрямую к вашему вопросу, но я привожу её для полноты:
Вторая причина — для лучшей документации
Вторая цель использования
typedef
— это предоставление лучшей документации для программы. Тип, называемыйTreeptr
, может быть легче понять, чем тот, который объявлен только как указатель на сложную структуру.
Как изменить цвет вывода echo в Linux
Разница между const int*, const int * const и int * const?
Почему переменные нельзя объявлять в операторе switch?
Как вывести список символов из .so файла?
`unsigned int` против `size_t`: когда и что использовать?