Почему не работает +++++ b?
int main ()
{
int a = 5,b = 2;
printf("%d",a+++++b);
return 0;
}
Этот код дает следующую ошибку:
error: lvalue, требуемое как операнд приращения
Но если я помещаю пробелы на протяжении a++ +
и ++b
, то он отлично работает.
int main ()
{
int a = 5,b = 2;
printf("%d",a++ + ++b);
return 0;
}
Что означает ошибка в первом примере?
Ответы
Ответ 1
printf("%d",a+++++b);
интерпретируется как (a++)++ + b
в соответствии с Правилом Максимального Мунка !.
++
(постфикс) не оценивает значение lvalue
, но требует, чтобы его операнд был lvalue
.
<суб > !
6.4/4 говорит
следующий токен предварительной обработки - это самая длинная последовательность символов, которая может составлять токен предварительной обработки "
Суб >
Ответ 2
Составители написаны поэтапно. Первый этап называется лексером и превращает символы в символическую структуру. Поэтому "++" становится чем-то вроде enum SYMBOL_PLUSPLUS
. Позже сценарий парсера превращает это в абстрактное синтаксическое дерево, но он не может изменить символы. Вы можете повлиять на лексер, вставив пробелы (которые заканчивают символы, если они не указаны в кавычках).
Нормальные лексеры жадные (за некоторыми исключениями), поэтому ваш код интерпретируется как
a++ ++ +b
Ввод в синтаксический анализатор представляет собой поток символов, поэтому ваш код будет выглядеть примерно так:
[ SYMBOL_NAME(name = "a"),
SYMBOL_PLUS_PLUS,
SYMBOL_PLUS_PLUS,
SYMBOL_PLUS,
SYMBOL_NAME(name = "b")
]
То, что считает синтаксический анализатор, синтаксически неверно. (EDIT на основе комментариев: семантически некорректно, потому что вы не можете применить ++ к r-значению, которое дает результат ++)
a+++b
есть
a++ +b
Это нормально. Вот и другие примеры.
Ответ 3
Лексер использует то, что обычно называют алгоритмом "максимум munch" для создания токенов. Это означает, что при чтении символов он продолжает считывать символы до тех пор, пока не встретит что-то, что не может быть частью того же токена, что и то, что у него уже есть (например, если оно считывает цифры, так что у него есть число, если оно встречается a A
, он знает, что не может быть частью числа, поэтому он останавливается и оставляет A
во входном буфере для использования в качестве начала следующего токена). Затем он возвращает этот токен парсеру.
В этом случае это означает, что +++++
получает lexed как a ++ ++ + b
. Поскольку первый пост-инкремент дает значение r, второй не может быть применен к нему, а компилятор дает ошибку.
Просто FWIW, на С++ вы можете перегрузить operator++
, чтобы получить lvalue, что позволяет это работать. Например:
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;
}
Скомпилирует и запускает (хотя и ничего не делает) с компиляторами С++, которые мне удобны (VС++, g++, Comeau).
Ответ 4
Этот точный пример описан в разделе 6.4 проекта стандарта C99 (те же подробности в C11). Пункт 4 лексических элементов гласит:
Если входной поток был проанализирован в токены предварительной обработки до заданного символа, следующий токен предварительной обработки - это самая длинная последовательность символов, которая может составлять токен предварительной обработки. [...]
которое также известно как правило максимального жаворонка, которое используется в лексическом анализе, чтобы избежать двусмысленности и работает, беря столько элементов, сколько возможно, чтобы сформировать действительный токен.
в этом параграфе также есть два примера, второй является точным соответствием вашему вопросу и выглядит следующим образом:
Пример 2. Фрагмент программы x +++++ y анализируется как x ++ ++ + y, что нарушает ограничение на операторы инкремента, даже если синтаксический анализ x ++ + ++ y может привести к правильному выражению.
что говорит нам о том, что:
a+++++b
будет проанализирован как:
a ++ ++ + b
что нарушает ограничения на постинкремент, поскольку результатом первого постинкремента является rvalue, а постинкремент требует lvalue. Это 6.5.2.4
в разделе 6.5.2.4
приращения и уменьшения Postfix, в которых говорится (выделено мое):
Операнд постфиксного оператора увеличения или уменьшения должен иметь квалифицированный или неквалифицированный вещественный или указательный тип и быть модифицируемым lvalue.
а также
Результатом оператора postfix ++ является значение операнда.
Книга C++ Gotchas также описывает этот случай в Gotcha #17
Максимальные проблемы Мунка, это та же проблема и в C++, а также приводятся некоторые примеры. Это объясняет это при работе со следующим набором символов:
->*
лексический анализатор может выполнять одно из трех действий:
- Рассматривайте это как три жетона:
-
, >
и *
- Считайте это двумя жетонами:
->
и *
- Считайте это одним знаком:
->*
Правило максимального жаворонка позволяет избежать этих двусмысленностей. Автор указывает, что это (в контексте C++):
решает гораздо больше проблем, чем вызывает, но в двух распространенных ситуациях это раздражает.
Первый пример - это шаблоны, аргументы которых также являются шаблонами (что было решено в C++ 11), например:
list<vector<string>> lovos; // error!
^^
Что интерпретирует закрывающие угловые скобки как оператор сдвига, поэтому для устранения неоднозначности требуется пробел:
list< vector<string> > lovos;
^
Во втором случае используются аргументы по умолчанию для указателей, например:
void process( const char *= 0 ); // error!
^^
будет интерпретироваться как *=
оператор присваивания, решение в этом случае заключается в названии параметров в объявлении.
Ответ 5
Ваш компилятор отчаянно пытается разобрать a+++++b
и интерпретирует его как (a++)++ +b
. Теперь результат post-increment (a++
) не является lvalue, т.е. Он не может быть снова добавлен.
Пожалуйста, никогда не пишите такой код в программах качества продукции. Подумайте о том, что бедный парень придет после вас, которому нужно интерпретировать ваш код.
Ответ 6
(a++)++ +b
a ++ возвращает предыдущее значение, значение r. Вы не можете увеличить это.
Ответ 7
Потому что это вызывает поведение undefined.
Какая из них?
c = (a++)++ + b
c = (a) + ++(++b)
c = (a++) + (++b)
Да, ни вы, ни компилятор этого не знаете.
Забастовкa >
EDIT:
Настоящая причина такова, как говорят другие:
Он интерпретируется как (a++)++ + b
.
но post increment требует lvalue (который является переменной с именем), но (a ++) возвращает значение r, которое не может быть увеличено, что приведет к появлению сообщения об ошибке.
спасибо другим, чтобы указать на это.
Ответ 8
Я думаю, что компилятор видит это как
c = ((a ++) ++) + b
++
должен иметь в качестве операнда значение, которое может быть изменено. a - значение, которое может быть изменено. a++
однако является "rvalue", его нельзя изменить.
Кстати, ошибка, которую я вижу на GCC C, такая же, но по-разному: lvalue required as increment operand
.