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


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

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

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

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

9 51

9 ответов:

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

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.

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

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

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

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

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

во-первых-для параметризации программы

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

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

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

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

надеюсь, что это помогает.

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

во-вторых-чтобы обеспечить лучшую документацию

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

есть много причин на это. Что я думаю:

  1. Typename становится короче и, следовательно, код также меньше и более читаемым.
  2. эффект сглаживания для более длинных имен структуре.
  3. Конвенция используется в частности команда / компании / Стиль.
  4. портирование - имеют одинаковое имя во всех ОС и машине. Его собственная структура данных может немного отличаться.

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

  1. сродни #define BEGIN { за исключением того, что он сохраняет некоторые набрав вместо того, чтобы делать больше.
  2. сродни #define FALSE 0. Если ваша идея "байт" является наименьшей адресуемой единицей,char байт по определению. Если ваша идея "байт" является октетом, то либо char тип октета, или ваша машина не имеет октетного типа.
  3. это действительно уродливая стенография для людей, которые не могут коснуться типа...
  4. - это ошибка. Он должен быть!-Еще -4--> или лучше uint32_t следует просто использовать непосредственно.
  5. это то же самое, что и 4. Заменить uint32_t С int16_t.

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

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

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

и некоторые директивы #define

pname_malloc, pname_strlen

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

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

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

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

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

надеюсь, что это помогает.