Источник: 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