Правило чтения по спирали операторов и объявлений языка C (C++) Печать
Добавил(а) microsin   

Техника, известная как «Чтение по спирали/по часовой стрелке» (“Clockwise/Spiral Rule”) позволяет любому программисту разобрать любое объявление языка Си. Взято на Хабре http://habrahabr.ru/blogs/cpp/100104/.

Следуйте этим простым шагам:

1. Начиная с неизвестного элемента, двигайтесь по спирали/по часовой стрелке; при этом заменяйте следующие элементы соответствующими фразами:

  • [X] или [] => массив размера X типа… или массив неопределённого размера типа...
  • (type1, type2) => функция, принимающая типы type1, type2 и возвращающая...
  • * => указатель на...


2. Двигайтесь по спирали/по часовой стрелке, пока не будут пройдены все элементы.

3. Всегда сначала надо разрешать выражения в скобках!

Пример 1: Простое объявление


                     +-------+
                     | +-+   |
                     | ^ |   |
                char *str[10];
                 ^   ^   |   |
                 |   +---+   |
                 +-----------+


Спросим себя, что такое str?

str — это...


Двигаемся по спирали/по часовой стрелке, начиная с str, и первый символ, который мы видим это '[', значит это массив, и так:

str — это массив размера 10 типа...


Продолжаем двигаться по спирали/по часовой стрелке, и следующее, что мы встречаем, это символ '*', значит у нас есть указатели, и так:

str — это массив размера 10 типа указатель на...


Двигаемся по спирали и видим конец строки, потому двигаемся дальше и видим тип 'char', и так:

str — это массив размера 10 типа указатель на char.


Мы посетили все элементы, т.е. всё, мы разобрали это выражение!

Пример 2: Объявление указателя на функцию


                     +--------------------+
                     | +---+              |
                     | |+-+|              |
                     | |^ ||              |
                char *(*fp)( int, float *);
                 ^   ^ ^  ||              |
                 |   | +--+|              |
                 |   +-----+              |
                 +------------------------+

Спросим себя, что такое 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.