В книге «Язык программирования C, 2-е издание» авторов Брайана В. Кернигана и Денниса М. Ритчи говорится о деклараторах и прямых деклараторах. Обсуждение начинается в книге на стр. 122 с dcl и direct-dcl Не могли бы вы объяснить разницу между декларатором и прямым декларатором простым и понятным способом? Что делает его прямым?
Также на с. 225
где direct-declarator является идентификатором или идентификатором в скобках. В частности, он не должен достигать типа функции с помощью typedef.
Мне кажется, что деклараторы — это объявление переменной или функции. В «TD» часть T указывает спецификаторы и типы, а часть D определяет идентификатор, то есть уникальное идентификационное имя переменной или функции. Это как-то связано с грамматикой языка.
Являются ли деклараторы косвенными, поскольку они не указаны как прямые, как в direct-declarator?
Различие действительно важно только для синтаксического анализа объявлений, на самом деле они не являются отдельными понятиями в языке. Термин «прямой декларатор» никогда не появляется за пределами синтаксической диаграммы.
@ Бармар, спасибо. Ваша диаграмма может быть полезна, когда я лучше знаком с темой, но я не понимаю синтаксическую диаграмму, и она не объясняет разницу между декларатором и прямым декларатором. Кроме того, я хочу понять грамматику. Я студент компьютерных наук.
Грубо говоря, декларатор — это полное объявление, в то время как прямой декларатор — это либо идентификатор сам по себе, либо идентификатор, за которым следует []
(делает его массивом) или ()
(делает функцию или указатель на функцию).
Полное определение этих терминов можно найти в синтаксисе декларатора, который можно найти в разделе 6.7.6p1 стандарта C11:
декларатор:
- указательopt прямой декларатор
прямой декларатор:
- идентификатор
(
декларатор)
- прямой-декларатор
[
список-классификаторов-типовoptвыражение-присваиванияopt]
- прямой-декларатор
[
static
список-классификаторов-типовoptвыражение-присваивания]
- прямой-декларатор
[
список-классификаторов-типовstatic
выражение-присваивания]
- прямой-декларатор
[
список-классификаторов-типовopt*
]
- прямой декларатор
(
список типов параметров)
- прямой-декларатор
(
список-идентификаторовopt)
указатель:
*
список-классификаторов-типовopt*
указатель-список-классификаторовoptсписок-квалификаторов-типов:
- квалификатор типа
- список-квалификаторов-типов
список типов параметров:
- список-параметров
- список параметров
,
...
список параметров:
- объявление параметра
- список-параметров
,
объявление-параметровобъявление параметра:
- декларатор спецификаторов объявлений
- спецификаторы объявления абстрактный деклараторopt
Даже второе издание K&R в наши дни представляет скорее исторический, чем практический интерес.
Тем не менее термины «декларатор» и «прямой декларатор» продолжают использоваться в текущей спецификации языка C. Спецификация языка описывает первый следующим образом:
Каждый декларатор объявляет один идентификатор и утверждает, что когда операнд той же формы, что и декларатор, появляется в выражении, он обозначает функцию или объект с областью действия, продолжительностью хранения и типом, указанными спецификаторами объявления.
(С17 6.7.6/2)
Из этого следует, что «спецификаторы объявления» отделены от «деклараторов». На самом деле декларатор — это часть объявления, определяющая, что объявляется.
«Прямые деклараторы» — это подмножество деклараторов. «Прямой» предназначен для контраста с «косвенным», как в указателях. Например, учитывая
int i;
int *p;
Все i
, *p
и p
являются синтаксическими деклараторами, но из них только i
и p
являются прямыми деклараторами. И только i
и *p
являются полными деклараторами, то есть теми, которые не появляются как часть другого декларатора (как это делает декларатор p
).
Это становится более сложным, чем это - например, заключение любого декларатора в круглые скобки дает прямой декларатор, даже если исходный декларатор сам по себе не является прямым.
В целом, это синтаксическое различие, о котором вам, вероятно, не нужно слишком беспокоиться, если только вы не пишете синтаксический анализатор для языка C. И если вы делаете это, то вам действительно нужно смотреть на формальное описание синтаксиса.
Грамматике требуется токен прямого декларатора, чтобы указать приоритет. Как бы то ни было, * foo [ 3 ]
должен анализироваться как декларатор, за которым следует *
прямой декларатор, которым является foo [ 3 ]
. Если бы грамматика не разделяла декларатор и прямой декларатор, было бы неясно, было ли это *
, за которым следует foo [ 3 ]
, сгруппированное вместе, или * foo
, сгруппированное вместе, за которым следует [ 3 ]
.
Грамматика говорит, что декларатор это:
где указатель — это *
, за которым следуют необязательные квалификаторы (например, const
), а прямой декларатор — один из:
(
декларатор )
[
список-классификаторов-типовoptвыражение-присваиванияopt]
[
static
список-классификаторов-типовoptвыражение-присваивания ]
[
список-классификаторов-типов static
выражение-присваивания ]
[
список-классификаторов-типовopt * ]
(
список типов параметров )
(
список-идентификаторовopt)
Итак, учитывая * foo [ 3 ]
, мы должны принять это как декларатор с *
для указателя и foo [ 3 ]
для прямого декларатора. Невозможно иметь *
в начале прямого декларатора. Таким образом, * foo [ 3 ]
должен объявлять массив из 3 указателей, а не указатель на массив из 3 элементов.
Если бы эти параметры для декларатора и прямого декларатора были объединены в один токен грамматики, то синтаксический анализ был бы неоднозначным. Вы можете проанализировать это как * foo [ 3 ]
, поскольку * foo
является декларатором, за которым следует [ 3 ]
, а это не то, чего мы хотим.
Имя не имеет большого значения; нам просто нужно другое имя для дополнительного токена. Есть и другие примеры этого в грамматике Си. Примечательно, что грамматика выражений начинается с выражения, а затем проходит через цепочку выражения-присваивания, условного-выражения, логического-ИЛИ-выражения и так далее. У них есть имена, связанные с операторами, которые они включают, пока вы не доберетесь до первичного выражения. Это имеет некоторое семантическое сходство с direct-declarator, предполагая, что они оба названы в духе «хорошо, мы добрались до конца этой грамматической цепочки, вот первичный/прямой токен».
Это действительно помогло: «Это имеет некоторое семантическое сходство с direct-declarator, предполагая, что они оба названы в духе «хорошо, мы добрались до конца этой грамматической цепочки, вот первичный/прямой токен».
Смотрите синтаксическую диаграмму здесь