Por que a +++++ b não funciona?


88
int main ()
{
   int a = 5,b = 2;
   printf("%d",a+++++b);
   return 0;
}

Este código fornece o seguinte erro:

erro: lvalue necessário como operando de incremento

Mas se eu colocar espaços em toda a extensão a++ +e ++b, funcionará bem.

int main ()
{
   int a = 5,b = 2;
   printf("%d",a++ + ++b);
   return 0;
}

O que o erro significa no primeiro exemplo?


3
É surpreendente depois de todo esse tempo que ninguém descobriu que a expressão exata que você está perguntando é usada como exemplo no padrão C99 e C11. Também dá uma boa explicação. Eu incluí isso na minha resposta.
Shafik Yaghmour

@ShafikYaghmour - Esse é o 'Exemplo 2' em C11 §6.4 Elementos Lexicais ¶6 . Ele diz "O fragmento do programa x+++++yé analisado como x ++ ++ + y, o que viola uma restrição dos operadores de incremento, embora a análise x ++ + ++ ypossa produzir uma expressão correta."
Jonathan Leffler

Respostas:


97

printf("%d",a+++++b);é interpretado como de (a++)++ + bacordo com a Regra Munch Máxima ! .

++(postfix) não avalia como um, lvaluemas requer que seu operando seja um lvalue.

! 6.4 / 4 diz que o próximo token de pré-processamento é a mais longa sequência de caracteres que poderia constituir um token de pré-processamento "


181

Os compiladores são escritos em etapas. O primeiro estágio é chamado de lexer e transforma os personagens em uma estrutura simbólica. Portanto, "++" se torna algo como um enum SYMBOL_PLUSPLUS. Posteriormente, o estágio do analisador transforma isso em uma árvore de sintaxe abstrata, mas não pode alterar os símbolos. Você pode afetar o lexer inserindo espaços (que terminam com os símbolos, a menos que estejam entre aspas).

Os lexers normais são gananciosos (com algumas exceções), então seu código está sendo interpretado como

a++ ++ +b

A entrada para o analisador é um fluxo de símbolos, então seu código seria algo como:

[ SYMBOL_NAME(name = "a"), 
  SYMBOL_PLUS_PLUS, 
  SYMBOL_PLUS_PLUS, 
  SYMBOL_PLUS, 
  SYMBOL_NAME(name = "b") 
]

Que o analisador considera sintaticamente incorreto. (EDITAR com base em comentários: Semanticamente incorreto porque você não pode aplicar ++ a um valor r, no qual um ++ resulta)

a+++b 

é

a++ +b

O que está ok. Assim como seus outros exemplos.


27
+1 Boa explicação. Eu tenho que apontar, porém: é sintaticamente correto, ele só tem um erro semântico (tentativa de incrementar o lvalue resultante de a++).

7
a++resulta em um rvalue.
Femaref de

9
No contexto de lexers, o algoritmo 'ganancioso' é geralmente chamado de Maximal Munch ( en.wikipedia.org/wiki/Maximal_munch ).
JoeG

14
Agradável. Muitas línguas têm casos semelhantes bizarros graças à lexing ganancioso. Aqui está um realmente estranho onde tornar a expressão mais longa a torna melhor: em VBScript x = 10&987&&654&&321é ilegal, mas estranhamente x = 10&987&&654&&&321é legal.
Eric Lippert,

1
Não tem nada a ver com ganância e tudo a ver com ordem e precedência. ++ é maior que +, então dois ++ serão feitos primeiro. +++++ b também será + ++ ++ be não ++ ++ + b. Crédito para @MByD pelo link.

30

O lexer usa o que geralmente é chamado de algoritmo de "mastigação máxima" para criar tokens. Isso significa que, à medida que está lendo os caracteres, ele continua lendo os caracteres até encontrar algo que não pode ser parte do mesmo token que já possui (por exemplo, se estiver lendo dígitos, então o que tem é um número, se encontrar an A, ele sabe que não pode ser parte do número. então ele para e deixa o Ano buffer de entrada para usar como o início do próximo token). Em seguida, ele retorna esse token para o analisador.

Nesse caso, isso significa que +++++é lexed como a ++ ++ + b. Como o primeiro pós-incremento produz um rvalue, o segundo não pode ser aplicado a ele e o compilador fornece um erro.

Apenas FWIW, em C ++ você pode sobrecarregar operator++para gerar um lvalue, o que permite que isso funcione. Por exemplo:

struct bad_code { 
    bad_code &operator++(int) { 
        return *this;
    }
    int operator+(bad_code const &other) { 
        return 1;
    }
};

int main() { 
    bad_code a, b;

    int c = a+++++b;
    return 0;
}

Compila e executa (embora não faça nada) com os compiladores C ++ que tenho à mão (VC ++, g ++, Comeau).


1
"por exemplo, se ele está lendo dígitos, então o que tem é um número, se encontrar um A, sabe que não pode ser parte do número" 16FAé um número hexadecimal perfeitamente fino que contém um A.
orlp

1
@nightcracker: sim, mas sem um 0xno início ainda o tratará como 16seguido por FA, não um único número hexadecimal.
Jerry Coffin,

@Jerry Coffin: Você não disse que não 0xfazia parte do número.
orlp

@nightcracker: não, não - visto que a maioria das pessoas não considera xum dígito, parecia totalmente desnecessário.
Jerry Coffin

14

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.


Você sabe que parte do C ++ 11 diz a regra de mastigação máxima? 2.2.3, 2.5.3 são interessantes, mas não tão explícitos quanto C. A >>regra é solicitada em: stackoverflow.com/questions/15785496/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功 veja esta resposta aqui
Shafik Yaghmour

Muito obrigado, é uma das seções que indiquei. Eu irei votar em você amanhã, quando meu boné acabar ;-)
Ciro Santilli 郝海东 冠状 病 六四 事件 事件 法轮功

12

Seu compilador tenta desesperadamente analisar a+++++be interpretar como (a++)++ +b. Agora, o resultado do pós-incremento ( a++) não é um lvalue , ou seja, não pode ser pós-incrementado novamente.

Nunca escreva esse código em programas de qualidade de produção. Pense no pobre sujeito que está vindo atrás de você e que precisa interpretar seu código.


10
(a++)++ +b

a ++ retorna o valor anterior, um rvalue. Você não pode incrementar isso.


7

Porque causa comportamento indefinido.

Qual é?

c = (a++)++ + b
c = (a) + ++(++b)
c = (a++) + (++b)

Sim, nem você nem o compilador sabem disso.

EDITAR:

O verdadeiro motivo é o dito pelos outros:

É interpretado como (a++)++ + b.

mas o incremento posterior requer um lvalue (que é uma variável com um nome), mas (a ++) retorna um rvalue que não pode ser incrementado, levando à mensagem de erro que você recebe.

Obrigado aos outros por apontar isso.


5
você poderia dizer o mesmo para a +++ b - (a ++) + be a + (++ b) têm resultados diferentes.
Michael Chinen de

4
na verdade, postfix ++ tem precedência maior do que prefixo ++, então a+++bsempre éa++ + b
MByD

4
Não acho que essa seja a resposta certa, mas posso estar errado. Acho que o lexer o define como algo a++ ++ +bque não pode ser analisado.
Lou Franco,

2
Eu discordo dessa resposta. 'comportamento indefinido' é bastante diferente da ambigüidade de tokenização; e também não acho que o problema seja.
Jim Blackler,

2
"Caso contrário, um +++++ b seria avaliada como ((a ++) ++) + b" ... minha visão agora é a+++++b não avaliar a (a++)++)+b. Certamente com o GCC, se você inserir esses colchetes e reconstruir, a mensagem de erro não mudará.
Jim Blackler,

5

Acho que o compilador vê isso como

c = ((a ++) ++) + b

++deve ter como operando um valor que pode ser modificado. a é um valor que pode ser modificado. a++no entanto, é um 'rvalue', não pode ser modificado.

Pela forma como o erro que eu ver em GCC C é o mesmo, mas de forma diferente redigida: lvalue required as increment operand.


0

Siga esta ordem de precessão

1. ++ (pré-incremento)

2. + - (adição ou subtração)

3. "x" + "y" adiciona a sequência

int a = 5,b = 2; printf("%d",a++ + ++b); //a is 5 since it is post increment b is 3 pre increment return 0; //it is 5+3=8

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.