int main ()
{
int a = 5,b = 2;
printf("%d",a+++++b);
return 0;
}
Ten kod powoduje następujący błąd:
błąd: wartość l wymagana jako operand inkrementacji
Ale jeśli wstawię spacje w całym a++ +
i ++b
, to działa dobrze.
int main ()
{
int a = 5,b = 2;
printf("%d",a++ + ++b);
return 0;
}
Co oznacza błąd w pierwszym przykładzie?
x+++++y
jest analizowany jakox ++ ++ + y
, co narusza ograniczenie operatorów inkrementacji, nawet jeśli analizax ++ + ++ y
może dać poprawne wyrażenie”.Odpowiedzi:
printf("%d",a+++++b);
jest interpretowane(a++)++ + b
zgodnie z regułą maksymalnego muncha ! .++
(postfiks) nie jest szacowany do an,lvalue
ale wymaga, aby jego operand byłlvalue
.! 6.4 / 4 mówi, że następny token przetwarzania wstępnego jest najdłuższą sekwencją znaków, która może stanowić token przetwarzania wstępnego "
źródło
Kompilatory są pisane etapami. Pierwszy etap to lekser i zamienia postacie w symboliczną strukturę. Więc „++” staje się czymś w rodzaju
enum SYMBOL_PLUSPLUS
. Później etap parsera zamienia to w abstrakcyjne drzewo składniowe, ale nie może zmienić symboli. Możesz wpłynąć na leksera, wstawiając spacje (które kończą symbole, chyba że są w cudzysłowach).Zwykłe leksery są chciwe (z pewnymi wyjątkami), więc twój kod jest interpretowany jako
Dane wejściowe do parsera to strumień symboli, więc Twój kod wyglądałby tak:
[ SYMBOL_NAME(name = "a"), SYMBOL_PLUS_PLUS, SYMBOL_PLUS_PLUS, SYMBOL_PLUS, SYMBOL_NAME(name = "b") ]
Która według parsera jest niepoprawna składniowo. (EDYCJA na podstawie komentarzy: semantycznie niepoprawna, ponieważ nie można zastosować ++ do wartości r, co skutkuje a ++)
jest
Co jest w porządku. Podobnie jak inne przykłady.
źródło
a++
).a++
daje w wyniku wartość r.x = 10&987&&654&&321
jest nielegalne, ale, co dziwne,x = 10&987&&654&&&321
jest legalne.Lekser do tworzenia tokenów używa tak zwanego algorytmu „maksymalnego żucia”. Oznacza to, że czytając znaki, czyta znaki, dopóki nie napotka czegoś, co nie może być częścią tego samego tokena, co już ma (np. Jeśli czyta cyfry, więc to, co ma, jest liczbą, jeśli napotka an
A
, wie, że nie może być częścią liczby, więc zatrzymuje się i pozostawiaA
w buforze wejściowym do użycia jako początek następnego tokenu). Następnie zwraca ten token do parsera.W tym przypadku oznacza to, że
+++++
jest leksykowany jakoa ++ ++ + b
. Ponieważ pierwszy post-inkrementacja daje wartość r, drugiej nie można do niej zastosować, a kompilator zgłasza błąd.Po prostu FWIW, w C ++ możesz przeciążać,
operator++
aby uzyskać lwartość, co pozwala na to. Na przykład: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; }
Kompiluje się i działa (choć nic nie robi) z kompilatorami C ++, które mam pod ręką (VC ++, g ++, Comeau).
źródło
16FA
to idealnie dokładna liczba szesnastkowa, która zawiera A.0x
na początku nadal potraktuje to jako16
następującą po nimFA
liczbę szesnastkową.0x
nie jest częścią numeru.x
cyfry, wydawało się to zupełnie niepotrzebne.Ten dokładny przykład jest omówiony w projekcie normy C99 (te same szczegóły w C11 ), sekcja 6.4 Elementy leksykalne, paragraf 4, który mówi:
który jest również znany jako reguła maksymalnego chrupania, która jest używana w analizie leksykalnej w celu uniknięcia niejednoznaczności i działa na podstawie jak największej liczby elementów, aby utworzyć prawidłowy token.
akapit zawiera również dwa przykłady, drugi jest dokładnym dopasowaniem do twojego pytania i wygląda następująco:
co mówi nam, że:
zostaną przeanalizowane jako:
co narusza ograniczenia dotyczące postinkrementacji, ponieważ wynikiem pierwszego postinkrementacji jest rvalue, a po inkrementacji wymaga lwartości. Jest to omówione w sekcji
6.5.2.4
Operatory przyrostowe i dekrementacja, które mówią ( moje podkreślenie ):i
Książka C ++ Gotchas również omawia ten przypadek w
Gotcha #17
Maximal Munch Problems, jest to ten sam problem również w C ++, a także podaje kilka przykładów. Wyjaśnia, że mając do czynienia z następującym zestawem znaków:analizator leksykalny może zrobić jedną z trzech rzeczy:
-
,>
i*
->
i*
->*
Zasada maksymalnego chrupania pozwala uniknąć tych niejednoznaczności. Autor zwraca uwagę, że to ( w kontekście C ++ ):
Pierwszym przykładem byłyby szablony, których argumenty szablonów są również szablonami ( co zostało rozwiązane w C ++ 11 ), na przykład:
list<vector<string>> lovos; // error! ^^
Który interpretuje zamykające nawiasy kątowe jako operator przesunięcia , więc do ujednoznacznienia wymagana jest spacja:
list< vector<string> > lovos; ^
Drugi przypadek dotyczy domyślnych argumentów wskaźników, na przykład:
void process( const char *= 0 ); // error! ^^
zostanie zinterpretowany jako
*=
operator przypisania, rozwiązaniem w tym przypadku jest nazwanie parametrów w deklaracji.źródło
>>
Twój kompilator desperacko próbuje przeanalizować
a+++++b
i interpretuje to jako plik(a++)++ +b
. Teraz wynik postinkrementacji (a++
) nie jest lwartością , tj. Nie może być ponownie zwiększony po inkrementacji.Nigdy nie pisz takiego kodu w programach jakości produkcji. Pomyśl o tym biednym człowieku, który cię ściga i musi zinterpretować twój kod.
źródło
a ++ zwraca poprzednią wartość, wartość r. Nie możesz tego zwiększyć.
źródło
Ponieważ powoduje niezdefiniowane zachowanie.Który to?
Tak, ani ty, ani kompilator tego nie wiecie.EDYTOWAĆ:
Prawdziwym powodem jest ten, o którym mówią inni:
Jest interpretowany jako
(a++)++ + b
.ale post-inkrementacja wymaga lwartości (która jest zmienną o nazwie), ale (a ++) zwraca wartość r, której nie można zwiększyć, co prowadzi do otrzymanego komunikatu o błędzie.
Podziękowania dla innych za zwrócenie uwagi.
źródło
a+++b
jest zawszea++ + b
a++ ++ +b
którego nie można przeanalizować.a+++++b
nie ocenia się(a++)++)+b
. Z pewnością w GCC, jeśli wstawisz te nawiasy i przebudujesz, komunikat o błędzie się nie zmieni.Myślę, że kompilator widzi to jako
c = ((a ++) ++) + b
++
musi mieć jako operand wartość, którą można modyfikować. a to wartość, którą można modyfikować.a++
jednak jest „wartością r”, nie można jej modyfikować.Nawiasem mówiąc, błąd, który widzę na GCC C jest taki sam, ale ma inne brzmienie:
lvalue required as increment operand
.źródło
Postępuj zgodnie z tą kolejnością
1. ++ (przed inkrementacją)
2. + - (dodawanie lub odejmowanie)
3. „x” + „y” dodaje obie sekwencje
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
źródło