Почему c = ++ (a + b) дает ошибку компиляции?
После исследования я прочитал, что оператор приращения требует, чтобы операнд имел модифицируемый объект данных: https://en.wikipedia.org/wiki/Increment_and_decrement_operators.
Из этого я предполагаю, что он дает ошибку компиляции, потому что (a+b)
является временным целым числом и поэтому не модифицируется.
Правильно ли это понимание? Это был мой первый случай, когда я пытался исследовать проблему, поэтому, если бы я кое-что искал, пожалуйста, посоветуйте.
Ответы
Ответ 1
Это просто правило, что все и, возможно, там, чтобы (1) упростить писать компиляторы C и (2) никто не убедил комитет стандартов C, чтобы его ослабить.
Неформально вы можете писать ++foo
если foo
может появиться в левой части выражения присваивания, например foo = bar
. Поскольку вы не можете писать a + b = bar
, вы не можете писать ++(a + b)
.
Нет никакой реальной причины, по которой a + b
не может дать временное, на котором может работать ++
, и результатом этого является значение выражения ++(a + b)
.
Ответ 2
Стандарт C11 гласит в разделе 6.5.3.1
Операнд префиксного оператора увеличения или уменьшения должен иметь атомарный, квалифицированный или неквалифицированный вещественный или указательный тип и должен быть изменяемым значением lvalue.
И "модифицируемое lvalue" описано в подразделе 6.3.2.1 подраздела 1
Lvalue - это выражение (с типом объекта, отличным от void), которое потенциально обозначает объект; если lvalue не обозначает объект при его оценке, поведение не определено. Когда говорят, что объект имеет определенный тип, тип определяется значением l, используемым для обозначения объекта. Изменяемое lvalue - это lvalue, которое не имеет типа массива, не имеет неполного типа, не имеет типа с константой, и если это структура или объединение, не имеет какого-либо члена (включая, рекурсивно, любой член или элемент всех содержащихся агрегатов или объединений) с типом, определенным const.
Таким образом, (a+b)
не является изменяемым значением lvalue и поэтому не подходит для оператора приращения префикса.
Ответ 3
Ты прав. ++
пытается присвоить новое значение исходной переменной. Таким образом, ++a
примет значение a
, добавит 1
к нему, а затем назначит его a
. Поскольку, как вы сказали, (a + b) является временным значением, а не переменной с назначенным адресом памяти, назначение не может быть выполнено.
Ответ 4
Думаю, вы в основном отвечали на свой вопрос. Я мог бы внести небольшое изменение в вашу формулировку и заменить "временную переменную" на "rvalue", как упоминал К. Гиббонс.
Термины variable, аргумент, временная переменная и т.д. Станут более ясными, когда вы узнаете о модели памяти C (это выглядит как хороший обзор: https://www.geeksforgeeks.org/memory-layout-of-c-program/).
Термин "rvalue" может показаться непрозрачным, когда вы только начинаете, поэтому я надеюсь, что следующее поможет в развитии интуиции об этом.
Lvalue/rvalue говорят о разных сторонах знака равенства (оператор присваивания): lvalue = левая сторона (нижний регистр L, а не один) rvalue = правая сторона
Немного узнать о том, как C использует память (и регистры), будет полезно узнать, почему важное значение имеет различие. В широких мазках кисти компилятор создает список инструкций машинного языка, которые вычисляют результат выражения (rvalue), а затем помещает этот результат где-нибудь (значение lvalue). Представьте, что компилятор имеет дело со следующим фрагментом кода:
x = y * 3
В сборочном псевдокоде он может выглядеть примерно так:
load register A with the value at memory address y
load register B with a value of 3
multiply register A and B, saving the result in A
write register A to memory address x
Оператору ++ (и его-аналогу) нужно "где-то" изменить, по существу, все, что может работать как lvalue.
Понимание модели памяти C будет полезно, потому что вы лучше поймете, как аргументы передаются функциям и (в конечном итоге), как работать с распределением динамической памяти, например функцией malloc(). По тем же причинам вы могли бы изучить некоторое простое программирование сборок в какой-то момент, чтобы лучше понять, что делает компилятор. Также, если вы используете gcc, параметр -S "Остановитесь после стадии компиляции, не собирайтесь ". может быть интересным (хотя я бы рекомендовал попробовать его на небольшом фрагменте кода).
Как и в стороне: инструкция ++ существует с 1969 года (хотя она началась с предшественника C, B):
(Ken Thompon's) (было), что перевод ++x был меньше, чем у x = x + 1 ".
Перевод аналогичен компиляции здесь, но они говорят о необычном аппаратном обеспечении, PDP-7. Следуя этой ссылке в Википедии, вы познакомитесь с интересной записью Денниса Ритчи ("R" в "K & R C") по истории языка C, которую здесь можно найти для удобства: http://www.bell-labs.com/usr/dmr/www/chist.html, где вы можете искать "++".
Ответ 5
Причина в том, что стандарт требует, чтобы операнд был lvalue. Выражение (a+b)
не является lvalue, поэтому применение оператора инкремента не допускается.
Теперь можно сказать: "Хорошо, это действительно причина, но на самом деле нет никакой реальной причины, кроме этого", но, к несчастью, конкретная формулировка того, как оператор фактически работает, требует, чтобы это было так.
Выражение ++E эквивалентно (E+ = 1).
Очевидно, вы не можете записать E += 1
если E
не является lvalue. Какой позор, потому что можно было просто сказать: "увеличивает E на единицу" и делаться. В этом случае применение оператора на не-lvalue было бы (в принципе) вполне возможным, за счет того, что компилятор был немного более сложным.
Теперь определение может быть тривиально переформулировано (я думаю, что это даже не изначально C, а реликвия B), но при этом коренным образом изменит язык на то, что больше не совместимо с его прежними версиями. Поскольку возможная выгода довольно мала, но возможные последствия огромны, чего никогда не было и, вероятно, никогда не произойдет.
Если вы считаете C++ в дополнение к C (вопрос отмечен C, но была дискуссия о перегрузках операторов), история становится еще более сложной. В C трудно представить, что это может быть так, но в C++ результат (a+b)
вполне может быть тем, что вы не можете увеличивать вообще, или приращение может иметь очень значительные побочные эффекты (не только добавив 1). Компилятор должен иметь возможность справиться с этим и диагностировать проблемные случаи по мере их возникновения. На lvalue, что все еще любопытно тривиально проверить. Не так для любого случайного выражения внутри круглой скобки, которую вы бросаете на бедную вещь.
Это не настоящая причина, почему это невозможно сделать, но это, безусловно, дает объяснение, почему люди, которые это осуществили, не совсем в восторге, чтобы добавить такую функцию, которая обещает очень мало пользы для очень немногих людей.
Ответ 6
Когда выполняется выражение ++ (a + b), то, например:
int a, b;
a = 10;
b = 20;
/* NOTE :
//step 1: expression need to solve first to perform ++ operation over operand
++ ( exp );
// in your case
++ ( 10 + 20 );
// step 2: result of that inc by one
++ ( 30 );
// here, you're applying ++ operator over constant value and it invalid use of ++ operator
*/
++(a+b);
Ответ 7
(a + b) оценивает значение r, которое не может быть увеличено.
Ответ 8
++ пытается присвоить значение исходной переменной, и поскольку (a + b) является временным значением, он не может выполнить операцию. И они в основном являются правилами конвенций программирования C, чтобы упростить программирование. Это.