В 1994 году Дэвид Андерсон опубликовал в comp.lang.c технику под названием «Clockwise/Spiral Rule». Она позволяет любому программисту на C мысленно разобрать сколь угодно запутанное объявление. Правило состоит из трёх простых шагов. Начинаешь с неизвестного элемента и движешься по спирали по часовой стрелке. Встречая токены, сразу заменяешь их английскими фразами: [X] или [] — «массив размера X» или «массив неопределённого размера», (type1, type2) — «функция, принимающая type1 и type2 и возвращающая», * — «указатель(и) на». Продолжаешь движение, пока не обойдёшь все токены. Если встречаешь скобки — сначала раскрываешь всё внутри них по тому же правилу.
Самый простой пример: char *str[10]; Спрашиваешь себя: что такое str? Начинаешь со str, движешься по спирали, первый символ — [, значит, «str — это массив из 10». Дальше * — «указателей на». Последний токен — char, итог: «str — это массив из 10 указателей на char».
Сложность растёт с указателями на функции: char *(*fp)( int, float *); Зажатый скобками fp сначала разрешается внутри них. (*fp) — «fp — это указатель на». Выныриваешь из скобок, следующий токен ( — «функцию, принимающую int и указатель на float, возвращающую». Продолжаешь по спирали, видишь * — «указатель на». И наконец char — «char». Результат: «fp — это указатель на функцию, принимающую int и указатель на float, возвращающую указатель на char».
Андерсон не побоялся привести «ультимативный» пример: void (*signal(int, void (*fp)(int)))(int); Первым делом разбираем вложенный fp. Движение внутри скобок даёт * — «указатель на». Потом ( — «функцию, принимающую int, возвращающую». Последнее слово — void — значит, ничего не возвращает: fp — это указатель на функцию, принимающую int и возвращающую void. Возвращаемся к signal. Уже внутри скобок видим ( — «signal — это функция, принимающая int и указатель на функцию, принимающую int и возвращающую void, возвращающая». После скобок встречаем * — «указатель на». Затем снова ( — «функцию, принимающую int, возвращающую». Завершает void — «ничего». Итоговая расшифровка: signal — это функция, принимающая int и указатель на функцию, принимающую int и возвращающую void, возвращающая указатель на функцию, принимающую int и возвращающую void.
Правило прекрасно работает с const и volatile. Для const char *chptr; выходит: chptr — это указатель на константный char. Для char * const chptr; — chptr — это константный указатель на char. А volatile char * const chptr; читается как константный указатель на volatile char. Андерсон советовал оттачивать приём на объявлениях со страницы 122 второго издания K&R.