Este exemplo exato é coberto no esboço da norma C99 ( mesmos detalhes em C11 ) seção 6.4 Elementos lexicais, parágrafo 4, que diz:
Se o fluxo de entrada foi analisado em tokens de pré-processamento até um determinado caractere, o próximo token de pré-processamento é a mais longa sequência de caracteres que poderia constituir um token de pré-processamento. [...]
que também é conhecida como regra de mastigação máxima, usada na análise lexical para evitar ambigüidades e funciona pegando o máximo de elementos possível para formar um token válido.
o parágrafo também tem dois exemplos, o segundo é uma correspondência exata para sua pergunta e é o seguinte:
EXEMPLO 2 O fragmento de programa x +++++ y é analisado como x ++ ++ + y, o que viola uma restrição nos operadores de incremento, embora a análise x ++ + ++ y possa produzir uma expressão correta.
que nos diz que:
a+++++b
será analisado como:
a ++ ++ + b
que viola as restrições do incremento posterior, uma vez que o resultado do primeiro incremento posterior é um rvalue e o incremento posterior requer um lvalue. Isso é abordado na seção 6.5.2.4
Operadores de incremento e decremento do Postfix que diz ( ênfase minha ):
O operando do operador de incremento ou decremento postfix deve ter o tipo real ou ponteiro qualificado ou não qualificado e deve ser um lvalue modificável.
e
O resultado do operador postfix ++ é o valor do operando.
O livro C ++ Gotchas também cobre este caso em Gotcha #17
Maximal Munch Problems . É o mesmo problema em C ++ e também dá alguns exemplos. Isso explica ao lidar com o seguinte conjunto de caracteres:
->*
o analisador léxico pode fazer uma das três coisas:
- Tratá-lo como três fichas:
-
, >
e*
- Trate-o como dois tokens:
->
e*
- Trate-o como um token:
->*
A regra da mastigação máxima permite evitar essas ambigüidades. O autor aponta que ( no contexto C ++ ):
resolve muito mais problemas do que causa, mas em duas situações comuns, é um incômodo.
O primeiro exemplo seriam modelos cujos argumentos de modelo também são modelos (o que foi resolvido em C ++ 11 ), por exemplo:
list<vector<string>> lovos; // error!
^^
Que interpreta os colchetes angulares de fechamento como o operador de deslocamento e, portanto, um espaço é necessário para desambiguar:
list< vector<string> > lovos;
^
O segundo caso envolve argumentos padrão para ponteiros, por exemplo:
void process( const char *= 0 ); // error!
^^
seria interpretado como *=
operador de atribuição, a solução neste caso é nomear os parâmetros na declaração.