Por que a gramática BNF de C permite declarações com uma sequência vazia de init-declarators?

28

Ao examinar a gramática BNF de C, achei estranho que a regra de produção de uma declaração tivesse essa aparência (de acordo com https://cs.wmich.edu/~gupta/teaching/cs4850/sumII06/The%20syntax%20of% 20C% 20in% 20Backus-Naur% 20form.htm ):

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

Por que usar um *quantificador (significando zero ou mais ocorrências) para o init-declarator? Isso permite que instruções como int;ou void;sejam sintaticamente válidas, mesmo que sejam semanticamente inválidas. Eles não poderiam ter usado apenas um +quantificador (uma ou mais ocorrências) em vez de *na regra de produção?

Tentei compilar um programa simples para ver o que o compilador gera e tudo o que ele faz é emitir um aviso.

Entrada:

int main(void) {
    int;
}

Resultado:

test.c: In function main’:
test.c:2:5: warning: useless type name in empty declaration
     int;
     ^~~
rafaelfp
fonte
2
A diferença é que o BNF define apenas a sintaxe. Muitas coisas são sintaticamente permitidas, mas ainda são inválidas (ou absurdas).
larkey 04/04
7
Ah, e por favor use intcomo um tipo de retorno para maine não use ()como uma lista de tipos de parâmetros em funções, mas (void)sim.
larkey 4/04
11
Conceitualmente , não há nada realmente errado nisso, exceto que isso soa um pouco engraçado: é basicamente perguntar ao computador "Gostaria de zero variáveis ​​int, por favor, nomes: [emptyset].". Afinal, você pode pedir zero maçã a alguém (embora isso provavelmente provoque uma reação um pouco mais interessante do que pedir uma, mas não é uma afirmação inerentemente sem sentido). Portanto, por que deveria ser não gramatical em C? Não há nada de errado com esse tipo de gramática.
The_Sympathizer
Muitas vezes, as coisas funcionam muito melhor quando incluímos o caso vazio (ou talvez o vácuo?).
The_Sympathizer
Às vezes, não é um humano que escreve um programa, mas outro programa. Às vezes, esse programa pode imprimir "int", seguido de uma lista separada por vírgulas dos nomes de usuário de que precisamos, seguida por ";" e fique feliz por não precisar verificar se a lista está vazia primeiro.
Hagen von Eitzen

Respostas:

29

declaration-specifierinclui type-specifier, o que inclui enum-specifier. Uma construção como

enum stuff {x, y};

é um válido declarationsem init-declarator.

Construções como int;são descartadas por restrições além da gramática :

Uma declaração que não seja uma declaração static_assert deve declarar pelo menos um declarador (que não seja os parâmetros de uma função ou os membros de uma estrutura ou união), uma tag ou os membros de uma enumeração.

Eu acho que existem razões de compatibilidade com versões anteriores por trás do seu compilador apenas emitir um aviso.

user2357112 suporta Monica
fonte
14

Uma declaração sem um declarador init:

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

é inofensivo para as listas de especificadores de declaração que não são um único especificador enum/ struct/ unione combina de maneira útil com as que são.

De qualquer forma, a gramática apresentada também corresponderá erroneamente a declarações como int struct foo x;or double _Bool y;(permite vários especificadores para corresponder a coisas como long long int), mas todos esses podem ser detectados posteriormente, em uma verificação semântica.

A própria gramática da BNF não eliminará todas as construções ilegais.

PSkocik
fonte