Dlaczego gramatyka BNF języka C dopuszcza deklaracje z pustą sekwencją deklaratorów init?

28

Przeglądając gramatykę BNF języka C, pomyślałem, że to dziwne, że reguła produkcyjna deklaracji wygląda tak (zgodnie z https://cs.wmich.edu/~gupta/teaching/cs4850/sumII06/The%20syntax%20of% 20C% 20in% 20 Backus-Naur% 20form.htm ):

<declaration> ::=  {<declaration-specifier>}+ {<init-declarator>}* ;

Po co używać *kwantyfikatora (co oznacza zero lub więcej wystąpień) dla init-declarator? Dzięki temu instrukcje takie jak int;lub void;mogą być poprawne pod względem składniowym, nawet jeśli są semantycznie niepoprawne. Czy nie mogli po prostu zastosować +kwantyfikator (jedno lub więcej wystąpień) zamiast *w regule produkcyjnej?

Próbowałem skompilować prosty program, aby zobaczyć, co wyprowadza kompilator, i wszystko, co robi, to ostrzeżenie.

Wejście:

int main(void) {
    int;
}

Wynik:

test.c: In function main’:
test.c:2:5: warning: useless type name in empty declaration
     int;
     ^~~
rafaelfp
źródło
2
Różnica polega na tym, że BNF definiuje tylko składnię. Dosyć wiele rzeczy jest składniowo dozwolonych, ale wciąż nieważnych (lub absurdalnych) C. Jednak fajne znalezisko!
larkey
7
Ach, i proszę użyć intjako typu zwrotu dla maini nie używaj ()jako listy typów parametrów w funkcjach, ale (void)zamiast tego.
larkey
1
Pod względem koncepcyjnym nie ma w tym nic złego, poza tym, że brzmi to trochę śmiesznie: w zasadzie pyta komputer: „Chciałbym zerowych zmiennych int, proszę, nazw: [emptyset].”. W końcu możesz poprosić kogoś o zero jabłek (chociaż prawdopodobnie wywoła to nieco ciekawszą reakcję niż prośba o jedno, ale nie jest to z natury nonsensowne stwierdzenie). Dlatego dlaczego powinno to być nie gramatyczne w C? Z tego rodzaju gramatyką nie ma nic złego.
The_Sympathizer
Bardzo często rzeczy działają o wiele ładniej, jeśli weźmiemy pod uwagę pustą (a może próżnię?) Obudowę.
The_Sympathizer
Czasami to nie człowiek pisze program, ale inny program. Taki program może czasami chcieć wydrukować „int”, a następnie oddzieloną przecinkami listę piątych potrzebnych imion, a następnie „;” i ciesz się, że nie musisz najpierw sprawdzać, czy wspomniana lista jest pusta.
Hagen von Eitzen,

Odpowiedzi:

29

declaration-specifierobejmuje type-specifier, co obejmuje enum-specifier. Konstrukt jak

enum stuff {x, y};

jest ważny declarationbez init-declarator.

Konstrukcje takie int;są wykluczone przez ograniczenia wykraczające poza gramatykę :

Deklaracja inna niż deklaracja static_assert deklaruje co najmniej deklarator (inny niż parametry funkcji lub elementy struktury lub unii), znacznik lub elementy wyliczenia.

Sądzę, że istnieją kompilacje wstecznej kompatybilności, które powodują, że Twój kompilator wydaje tylko ostrzeżenie.

user2357112 obsługuje Monikę
źródło
14

Deklaracja bez deklaratora init:

<declaration> ::=  {<declaration-specifier>}+ {<init-declarator>}* ;

jest nieszkodliwy dla list specyfikatorów deklaracji, które nie są pojedynczymi specyfikatorami enum/ struct/ unioni użytecznie pasuje do tych, które są.

W każdym razie prezentowana gramatyka będzie również błędnie dopasowywać deklaracje takie jak int struct foo x;lub double _Bool y;(pozwala wielu specyfikatorom w celu dopasowania rzeczy takich jak long long int), ale wszystkie te można wykryć później, w ramach kontroli semantycznej.

Sama gramatyka BNF nie usunie wszystkich nielegalnych konstrukcji.

PSkocik
źródło