Как вы читаете объявления C?
Я слышал о некоторых методах, но ни один из них не застрял. Лично я стараюсь избегать сложных типов в C и пытаюсь разбить их на компонент typedef.
Теперь я сталкиваюсь с необходимостью поддерживать некоторый унаследованный код от так называемого "трехзвездного программиста", и мне трудно читать часть кода *** [][].
Как вы читаете сложные объявления C?
12 ответов
В этой статье объясняются относительно простые 7 правил, которые позволят вам прочитать любое объявление C, если вы захотите или хотите сделать это вручную: http://www.ericgiguere.com/articles/reading-c-declarations.html
- Найдите идентификатор. Это ваша отправная точка. На листе бумаги напишите "объявить идентификатор как".
- Посмотри направо. Если там ничего нет или есть правильная скобка ")", перейдите к шагу 4.
Теперь вы находитесь в дескрипторе массива (левая скобка) или функции (левая скобка). Их может быть последовательность, заканчивающаяся либо несоответствующей правой круглой скобкой, либо концом объявления (точка с запятой или "=" для инициализации). Для каждого такого дескриптора, читая слева направо:
- если пустой массив "[]", напишите "массив из"
- если массив с размером, напишите "размер массива"
- если функция "()", напишите "возвращение функции"
Остановитесь на непревзойденных скобках или конце объявления, в зависимости от того, что произойдет раньше.
- Вернитесь в исходное положение и посмотрите налево. Если там ничего нет или есть левая скобка "(", перейдите к шагу 6.
- Теперь вы находитесь в дескрипторе указателя "*". Это может быть последовательность слева, заканчивающаяся либо непревзойденной левой круглой скобкой "(", либо началом декларатора. Чтение справа налево, для каждого дескриптора указателя напишите "указатель на". Остановитесь в непревзойденной круглой скобке или начало объявления, в зависимости от того, что является первым.
- На данный момент у вас есть выражение в скобках или полный декларатор. Если у вас есть выражение в скобках, рассмотрите его как новую отправную точку и вернитесь к шагу 2.
- Запишите спецификатор типа. Стоп.
Если у вас все в порядке с инструментом, я предлагаю вам воспользоваться этой программой. 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 (и 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) ();
Объявите это так, как вы это используете.
Распространенные проблемы читабельности включают указатели на функции и тот факт, что массивы действительно являются указателями, а многомерные массивы - это действительно одномерные массивы (которые действительно являются указателями). Надеюсь, что это помогает некоторым.
В любом случае, когда вы понимаете объявления, возможно, вы можете найти способ упростить их, чтобы сделать их более читабельными для следующего парня.
Читайте справа налево.
***code[][]
- код [] [] является многомерным массивом
- * code [] [] - указатель на многомерный массив
- ** code [] [] - указатель многомерного массива на указатель
- *** code [] [] - это многомерный массив указателей на указатель на указатель