Почему программисты C используют typedefs для переименования базовых типов?

Итак, я далек от эксперта по C, но что-то меня раздражало про код, который я читал в течение долгого времени: может кто-нибудь объяснить мне, почему программисты C (++) используют typedefs для переименования простых типов? Я понимаю, почему вы использовали их для structs, но в чем именно причина объявлений, которые я вижу как

typedef unsigned char uch;
typedef uch UBYTE;
typedef unsigned long ulg;
typedef unsigned int u32;
typedef signed short s16;

Есть ли какое-то преимущество для этого, что мне не ясно (программист, чей опыт начинается с Java и не отважился далеко за пределами строгого типа языков)? Потому что я не могу придумать никаких причин для этого - похоже, что это просто сделает код менее читаемым для людей, незнакомых с проектом.

Не стесняйтесь относиться ко мне как к новичкам C, я, честно говоря, очень мало знаю об этом, и, вероятно, есть вещи, которые я неправильно понял с самого начала.;)

Ответ 1

Переименование типов без изменения их открытой семантики/характеристик не имеет большого смысла. В вашем примере

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, программисту нужно будет только изменить одно определение typedef. UBYTE, по-видимому, означает неподписанный машинный байтовый тип, что не обязательно unsigned char.

Стоит отметить, что спецификация C99 уже предоставляет стандартную номенклатуру для интегральных типов определенной ширины, например int16_t, uint32_t и т.д. Вероятно, имеет смысл придерживаться этого стандартного соглашения об именах на платформах, которые не поддерживают C99.

Ответ 2

Это позволяет переносить. Например, вам нужен 32-разрядный целочисленный тип без знака. Какой стандартный тип? Вы не знаете - это реализация определена. Вот почему вы typedef отдельный тип должны быть 32-разрядным целым без знака и использовать новый тип в вашем коде. Когда вам нужно скомпилировать другую версию C, вы просто изменяете typedef s.

Ответ 3

Иногда он используется для уменьшения громоздкой вещи, например volatile unsigned long, к чему-то более компактному, например vuint32_t.

В других случаях это помогает с переносимостью, поскольку такие типы, как int, не всегда одинаковы на каждой платформе. Используя typedef, вы можете установить класс хранения, который вас интересует, в ближайшее совпадение платформы, не изменяя весь исходный код.

Ответ 4

Ниже приведена цитата из языка программирования C (K & R)

Помимо чисто эстетических проблем, есть две основные причины для использования Определения типов.

Сначала - для параметризации программы

Во-первых, это параметризация программы по проблемам переносимости. Если typedefs используются для типов данных которые могут быть зависящими от машины, только для типизации требуется изменение, когда программа перемещается.

Одной из распространенных ситуаций является использование typedef-имен для различных целых чисел количества, затем набор вариантов short, int и long для каждой хост-машины. Типы, подобные size_t и ptrdiff_t из стандартной библиотеки являются примерами.

Курсивные части говорят нам, что программисты typedef базовый тип для переносимости. Если я хочу, чтобы моя программа работала на разных платформах, используя другой компилятор, я постараюсь обеспечить ее переносимость всеми возможными способами и typedef - это один из них.

Когда я начал программировать с использованием компилятора Turbo C на платформе Windows, он дал нам размер int 2. Когда я перешел на платформу Linux и GCC, размер, который я получаю, равен 4. Если бы я разработал программу, использующую Turbo C, который полагался на утверждение, что sizeof( int ) всегда два, он не портировал бы должным образом на мою новую платформу.

Надеюсь, что это поможет.

Следующая цитата из K & R не связана с вашим запросом, но я отправил ее также для завершения.

Вторые - для обеспечения лучшей документации

Вторая цель typedefs - предоставить лучшую документацию для программа - тип, называемый Treeptr, может быть легче понять, чем объявленный только как указатель на сложную структуру.

Ответ 5

Большинство этих шаблонов - это плохая практика, возникающая при чтении и копировании существующего плохого кода. Часто они отражают недоразумения о том, что C делает или не требует.

  • Сродствует #define BEGIN {, кроме того, что он сохраняет некоторую типизацию вместо того, чтобы делать больше.
  • Сродствует #define FALSE 0. Если ваша идея "байт" - наименьшая адресуемая единица, char является байтом по определению. Если ваша идея "байта" является октетом, то либо char является октетом, либо ваш тип не имеет октета.
  • Действительно уродливая стенография для людей, которые не могут касаться типа...
  • Это ошибка. Он должен быть typedef uint32_t u32; или еще лучше, uint32_t должен использоваться непосредственно.
  • То же самое, что и 4. Заменить uint32_t на int16_t.

Положите на них штамп считающийся вредным. typedef следует использовать, когда вам действительно нужно создать новый тип, определение которого может измениться в течение жизненного цикла вашего кода или когда код переносится на другое оборудование, а не потому, что вы думаете, что C будет "красивее" с разными именами типов.

Ответ 6

Есть много причин. Я думаю:

  • Типичное имя становится короче и, следовательно, код также меньше и читабельнее.
  • Анимационный эффект для более длинных имен структур.
  • Конвенция, используемая в частности для команды/компаний/стиля.
  • Портирование - имеет одинаковое имя для всех ОС и компьютеров. Его собственная структура данных может несколько отличаться.

Ответ 7

Мы используем его, чтобы сделать его проектом/платформой, все имеет общее соглашение об именах

pname_int32, pname_uint32, pname_uint8 - pname - имя проекта/платформы/модуля

И некоторые #defines

pname_malloc, pname_strlen

Легче читать и сокращать длинные типы данных, такие как unsigned char, на pname_uint8, также делая это соглашение для всех модулей.

При портировании вам нужно просто изменить один файл, что упростит перенос.

Ответ 8

Чтобы сократить длинную историю, вы можете сделать это, чтобы сделать ваш код переносимым (с меньшими усилиями/редактированием). Таким образом, вы не зависите от "int", вместо этого вы используете INTEGER, который может быть любым, что вы хотите.

Ответ 9

Все типы [|u]intN_t, где N = 8 | 16 | 32 | 64 и т.д., определены для каждой архитектуры таким образом. Это прямое следствие того, что стандарт не гарантирует, что char, int, float и т.д. Имеют ровно N бит - это было бы безумным. Вместо этого стандарт определяет минимальные и максимальные значения каждого типа в качестве гарантий для программиста, а в разных типах архитектуры может значительно превышать эти границы. Это не редкость.

typedef в вашем сообщении используется для определенных типов определенной длины в определенной архитектуре. Вероятно, это не лучший выбор именования; u32 и s16 немного коротки, по-моему. Кроме того, было бы неплохо показать имена ulg и uch, можно было бы прикрепить их к конкретной строке приложения, так как они явно не будут отображаться.

Надеюсь, что это поможет.