Как вы читаете объявления C?

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

Теперь я сталкиваюсь с необходимостью поддерживать некоторый унаследованный код от так называемого "трехзвездного программиста", и мне трудно читать часть кода *** [][].

Как вы читаете сложные объявления C?

12 ответов

В этой статье объясняются относительно простые 7 правил, которые позволят вам прочитать любое объявление C, если вы захотите или хотите сделать это вручную: http://www.ericgiguere.com/articles/reading-c-declarations.html

  1. Найдите идентификатор. Это ваша отправная точка. На листе бумаги напишите "объявить идентификатор как".
  2. Посмотри направо. Если там ничего нет или есть правильная скобка ")", перейдите к шагу 4.
  3. Теперь вы находитесь в дескрипторе массива (левая скобка) или функции (левая скобка). Их может быть последовательность, заканчивающаяся либо несоответствующей правой круглой скобкой, либо концом объявления (точка с запятой или "=" для инициализации). Для каждого такого дескриптора, читая слева направо:

    • если пустой массив "[]", напишите "массив из"
    • если массив с размером, напишите "размер массива"
    • если функция "()", напишите "возвращение функции"

    Остановитесь на непревзойденных скобках или конце объявления, в зависимости от того, что произойдет раньше.

  4. Вернитесь в исходное положение и посмотрите налево. Если там ничего нет или есть левая скобка "(", перейдите к шагу 6.
  5. Теперь вы находитесь в дескрипторе указателя "*". Это может быть последовательность слева, заканчивающаяся либо непревзойденной левой круглой скобкой "(", либо началом декларатора. Чтение справа налево, для каждого дескриптора указателя напишите "указатель на". Остановитесь в непревзойденной круглой скобке или начало объявления, в зависимости от того, что является первым.
  6. На данный момент у вас есть выражение в скобках или полный декларатор. Если у вас есть выражение в скобках, рассмотрите его как новую отправную точку и вернитесь к шагу 2.
  7. Запишите спецификатор типа. Стоп.

Если у вас все в порядке с инструментом, я предлагаю вам воспользоваться этой программой. cdecl: http://gd.tuwien.ac.at/linuxcommand.org/man_pages/cdecl1.html

Я обычно использую то, что иногда называют "правилом по часовой стрелке". Это выглядит так:

  • Начните с идентификатора.
  • Идите прямо к нему справа.
  • Затем двигайтесь по часовой стрелке и идите налево.
  • Двигайтесь по часовой стрелке и идите направо.
  • Делайте это, пока объявление не было проанализировано полностью.

Есть дополнительное мета-правило, о котором нужно позаботиться:

  • Если есть скобки, завершите каждый уровень скобок, прежде чем выйти.

Здесь "идти" и "двигаться" где-то означает читать символ там. Правила для этого:

  • * - указатель на
  • () - функция возврата
  • (int, int) - функция, принимающая два целых и возвращающая
  • int, char, так далее. - int, char, так далее.
  • [] - массив
  • [10] - массив из десяти
  • и т.п.

Так, например, int* (*xyz[10])(int*, char) читается как:

XYZ является

массив из десяти

указатель на

функция, принимающая int* и char и возвращающая

int*

Одно слово: cdecl

Блин, побил на 15 секунд!

Cdecl (и C++decl) - это программа для кодирования и декодирования объявлений типов C (или C++).

http://gd.tuwien.ac.at/linuxcommand.org/man_pages/cdecl1.html

cdecl предлагает интерфейс командной строки, поэтому давайте попробуем:

cdecl> explain int ***c[][]
declare c as array of array of pointer to pointer to pointer to int

другой пример

 explain int (*IMP)(ID,SEL) 
declare IMP as pointer to function (ID, SEL) returning int

Однако в книге "C Deep Secrets" есть целая глава, озаглавленная "Расшифровка объявлений в C.

С уважением Фридрих

Когда я делал C, я использовал программу под названием "cdecl". Похоже, что он в Ubuntu Linux в пакете cutils или cdecl, и, вероятно, доступен в другом месте.

Только что наткнулся на светящийся раздел " Развитие языка Си":

Для каждого объекта такого составного типа уже был способ упомянуть базовый объект: индексировать массив, вызывать функцию, использовать оператор косвенности для указателя. Аналогичные рассуждения привели к объявлению синтаксиса для имен, отражающих синтаксис выражения, в котором имена обычно появляются. Таким образом,

int i, *pi, **ppi;

объявить целое число, указатель на целое число, указатель на указатель на целое число. Синтаксис этих объявлений отражает наблюдение, что все i, *pi и **ppi дают тип int при использовании в выражении. Так же,

int f(), *f(), (*f)();

объявить функцию, возвращающую целое число, функцию, возвращающую указатель на целое число, указатель на функцию, возвращающую целое число;

int *api[10], (*pai)[10];

объявить массив указателей на целые числа и указатель на массив целых чисел. Во всех этих случаях объявление переменной напоминает ее использование в выражении, тип которого указан в заголовке объявления.

Автоматизированное решение - cdecl.

В общем, вы объявляете переменную так, как вы ее используете. Например, вы разыменовываете указатель p как в:

символ с = * р

Вы заявляете об этом похожим образом:

char * p;

То же самое касается волосатых указателей функций. Давайте объявим f старым добрым "указателем на функцию, возвращающую указатель на int", а внешним объявлением просто смешно. Это указатель на функцию, поэтому мы начнем с:

extern * f ();

Он возвращает указатель на int, так что где-то впереди там есть

extern int * * f(); // XXX еще не совсем

Теперь, какая правильная ассоциативность? Я никогда не могу вспомнить, так что используйте некоторые скобки.

extern (int *) (* f) ();

Объявите это так, как вы это используете.

Есть также веб-версия cdecl, которая довольно приятна.

Распространенные проблемы читабельности включают указатели на функции и тот факт, что массивы действительно являются указателями, а многомерные массивы - это действительно одномерные массивы (которые действительно являются указателями). Надеюсь, что это помогает некоторым.

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

Читайте справа налево.

***code[][]
  • код [] [] является многомерным массивом
  • * code [] [] - указатель на многомерный массив
  • ** code [] [] - указатель многомерного массива на указатель
  • *** code [] [] - это многомерный массив указателей на указатель на указатель
Другие вопросы по тегам