Сложные объявления СИ

Сложные объявления СИ


Выше вы можете увидеть список объявлений на языке Си. Совсем недавно я не мог сказать, что они означают. Теперь же я могу вполне уверенно объяснить их, изучив правила приоритетов для объявлений в Си. 


В моём случае изучение сложные объявлений было важным шагом к использованию сложных структур данных и расширению своих возможностей. Если я не могу прочитать сложное объявление, я не смогу его использовать, а следовательно, мне придётся использовать худшие решения.


Я думал, что для усвоения этой темы мне придётся действовать по стандартной схеме «повторение — мать учения», но всё оказалось на удивление просто. Нужно запомнить лишь несколько правил — вот они, в порядке уменьшения приоритета:


Скобки, объединяющие части объявления.

Постфиксные операторы: круглые скобки (), обозначающие функцию, и квадратные [], обозначающие массив.

Префиксный оператор: звёздочка *, обозначающая указатель.

Начнём с четвертого объявления из моего списка.


char* foo[5];

Правила приоритетов говорят, что квадратные скобки старше указателя, поэтому foo — это массив указателей на символ, а не указатель на массив символов.


Вот пошаговый процесс применения правил к этому выражению. Начнём с имени foo: «foo — это…». Дальше идут квадратные скобки: «foo — это массив из пяти…». Осталась звёздочка: «foo — это массив из пяти указателей на…». Добавляем тип и получаем: «foo — это массив из пяти указателей на символ».


Если бы я хотел получить указатель на массив, то я должен был бы добавить круглые скобки:


char(* foo)[5];

// foo - это...

// foo - это указатель на...

// foo - это указатель на массив из пяти...

// foo - это указатель на массив из пяти символов

В этот раз круглые скобки повысили приоритет звёздочки, и мы получили указатель на массив.


В восьмом объявлении присутствует две пары круглых скобок:


char* (*foo)(char*);

// foo - это...

// foo - это указатель на...

// foo - это указатель на функцию, принимающую указатель на символ...

// foo - это указатель на функцию, принимающую указатель на символ, возвращающую указатель на...

// foo - это указатель на функцию, принимающую указатель на символ, возвращающую указатель на символ

Я немного сократил количество шагов, но так, на мой взгляд, даже проще. К параметрам функций правила применяются аналогично.


И последний пример — самое сложное объявление из списка:


char* (*(*foo[5])(char*))[];

// foo - это...

// foo - это массив из 5...

// foo - это массив из 5 указателей на...

// foo - это массив из 5 указателей на функцию, принимающую указатель на символ...

// foo - это массив из 5 указателей на функцию, принимающую указатель на символ и возвращающую указатель на...

// foo - это массив из 5 указателей на функцию, принимающую указатель на символ и возвращающую указатель на массив из...

// foo - это массив из 5 указателей на функцию, принимающую указатель на символ и возвращающую указатель на массив из указателей на...

// foo - это массив из 5 указателей на функцию, принимающую указатель на символ и возвращающую указатель на массив из указателей на символ

Стоит заметить, что последний пример является крайним случаем использования правил приоритетов, и на практике такое не приветствуется. Кроме того, обратите внимание, что в рассмотренных примерах не используются ключевые слова наподобие const и volatile, а также нет объявлений структур, перечислений и объединений.


Перевод статьи «Untangling Complex Declarations in C»

Report Page