Date Редакция Категория comp Теги C

Источник: The "Clockwise/Spiral Rule" By David Anderson. Перевод взят отсюда и слегка мною исправлен. На мой взгляд, это правило удобнее, чем приведенные в главе 5 K&R.

Данная техника, известная как "Clockwise/Spiral Rule" («Чтение по часовой стрелке/спирали») позволяет любому программисту разобрать любое объявление языка Си.

Для этого следуйте трём простым шагам:

  1. Начиная с неизвестного элемента, двигайтесь по спирали/по часовой стрелке; при этом заменяйте следующие элементы соответствующими фразами:
    • [X] или [] => массив размера X типа... или массив неопределённого размера типа...
    • (type1, type2) => функция, принимающая типы type1, type2 и возвращающая...
    • * => указатель на...
  2. Двигайтесь по спирали, пока не будут пройдены все элементы.
  3. Всегда сначала надо разрешать выражения в скобках!

Пример 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