Источник: The "Clockwise/Spiral Rule" By David Anderson. Перевод взят отсюда и слегка мною исправлен. На мой взгляд, это правило удобнее, чем приведенные в главе 5 K&R.
Данная техника, известная как "Clockwise/Spiral Rule" («Чтение по часовой стрелке/спирали») позволяет любому программисту разобрать любое объявление языка Си.
Для этого следуйте трём простым шагам:
- Начиная с неизвестного элемента, двигайтесь по спирали/по часовой стрелке; при этом заменяйте следующие элементы соответствующими фразами:
[X]или[]=> массив размераXтипа... или массив неопределённого размера типа...(type1, type2)=> функция, принимающая типыtype1,type2и возвращающая...*=> указатель на...
- Двигайтесь по спирали, пока не будут пройдены все элементы.
- Всегда сначала надо разрешать выражения в скобках!
Пример 1: Простое объявление
+-------+
| +-+ |
| ^ | |
char *str[10];
^ ^ | |
| +---+ |
+-----------+
Спросим себя: «Что такое str?»
str — это...
Движемся по спирали по часовой стрелке, начиная со str. И первый символ на нашем пути — [. Значит str — это массив, точнее...
str — это массив размера 10 типа...
Продолжаем двигаться по спирали, и следующее, что мы встречаем, это символ *. Значит мы имеем дело с указателями, так что...
str — это массив размера 10 типа указатель на...
Движемся по спирали и натыкаемся на конец строки (;). Разбирать его незачем, потому движемся дальше и видим тип char. Итак:
str — это массив размера 10 типа указатель на char.
Больше элементов нет. Мы посетили их все, а значит — разобрали это выражение!
Пример 2: Объявление указателя на функцию
+--------------------+
| +---+ |
| |+-+| |
| |^ || |
char *(*fp)( int, float *);
^ ^ ^ || |
| | +--+| |
| +-----+ |
+------------------------+
Cпросим себя, что такое fp?
fp — это...
Двинемся по спирали, и первое что мы видим, это ')'. Таким образом fp находится внутри скобок. Продолжаем спираль внутри скобок, и следующий символ на нашем пути — '*', то есть:
fp — это указатель на...
Теперь мы вне скобок и, продолжая движение по спирали, видим '(', т.е. это функция, а значит...
fp — это указатель на функцию, принимающую int и указатель на float, возвращающую...
Продолжая двигаться по спирали, встречаем символ '*', что означает, что...
fp — это указатель на функцию, принимающую int и указатель на float, возвращающую указатель на...
Продолжая движение по спирали, встречаем символ ';', но так как не все символы ещё обработаны, то движемся дальше, и встречаем 'char'. Итак:
fp — это указатель на функцию, принимающую int и указатель на float, возвращающую указатель на char.
Пример 3: Ultimate
+-----------------------------+
| +---+ |
| +---+ |+-+| |
| ^ | |^ || |
void (*signal(int, void (*fp)(int)))(int);
^ ^ | ^ ^ || |
| +------+ | +--+| |
| +--------+ |
+----------------------------------+
Спросим себя, что такое signal?
Обратите внимание — signal находится внутри скобок, так что сначала нужно разобрать выражение внутри них! Двигаясь по спирали, мы встречаем '(' и получаем, что...
signal — функция, принимающая int и ...
Хм, мы можем использовать наше правило для разбора элемента 'fp', Итак, что же такое fp? fp также стоит внутри скобок, так что продолжаем двигаться и находим '*'. Значит...
fp — это указатель на...
Двигаясь по спирали, встречаем '(', то есть
fp — это указатель на функцию, принимающую int, возвращающую...
Двигаясь по спирали, мы находим 'void', так что
fp — это указатель на функцию, принимающую int, возвращающую void.
Мы закончили с fp, теперь продолжим разбор signal. Сейчас мы имеем:
signal — функция, принимающая int и указатель на функцию, принимающую int, возвращающую void, возвращающая...
Мы всё ещё внутри скобок, так что следующий символ — это '*', а значит:
signal — функция, принимающая int и указатель на функцию, принимающую int, возвращающую void, возвращающая указатель на...
Мы разобрали элементы внутри скобок. Продолжая двигаться по спирали, получаем символ '(', что даёт нам...
signal — функция, принимающая int и указатель на функцию, принимающую int, возвращающую void, возвращающая указатель на функцию, принимающую int и возвращающую...
Наконец, продолжая движение, мы находим последний элемент — 'void'. Теперь, наше полное описание выглядит так:
signal — функция, принимающая int и указатель на функцию, принимающую int, возвращающую void, возвращающая указатель на функцию, принимающую int и возвращающую void.
И еще...
Рассмотренное нами правило применимо, в частности, для анализа выражений с атрибутами const и volatile. Например:
const char *chptr;
Итак, что такое chptr?
chptr — это указатель на char неизменяемый.
А как насчёт такого:
char * const chptr;
Что такое chptr?
chptr — это неизменяемый указатель на char.
И, наконец:
volatile char * const chptr;
Что такое chptr?
chptr — это неизменяемый указатель на char volatile.
Комментарии
comments powered by Disqus