Why does c = ++(a+b) give compilation error?
Asked Answered
C

8

113

After researching, I read that the increment operator requires the operand to have a modifiable data object: https://en.wikipedia.org/wiki/Increment_and_decrement_operators.

From this I guess that it gives compilation error because (a+b) is a temporary integer and so is not modifiable.

Is this understanding correct? This was my first time trying to research a problem so if there was something I should have looked for please advise.

Cullum answered 20/6, 2018 at 14:59 Comment(12)
That's not bad in terms of research. You are on the right track.Racy
What do you expect the expression to do?Underact
according to C11 standard 6.5.3.1: The operand of the prefix increment or decrement operator shall have atomic, qualified, or unqualified real or pointer type, and shall be a modifiable lvalueHardhearted
They are pre or post increment operators and do two things. They are not just for incrementing a variable. Typical use is c = s[i++]; where i acts as an index and you want to get a value in the array and increment the index, ( Not the value) The pre/post increments are notoriously bad if used in parameters to functions and are should be avoided in this case. I think you really want c = (a + b +1); Take the example a = b++; a is assigned the value of b and then b is incremented. It is doing two things.Jab
How would you like the 1 to be distributed between a and b? "Should array indices start at 0 or 1? My compromise of 0.5 was rejected without, I thought, proper consideration." — Stan Kelly-BootleHautesavoie
I think a follow on question is why would you ever want to do this when c = a + b + 1 makes your intent clearer and is also shorter to type. The increment/decrement operators do two things 1. they and their argument form an expression (that can be used, e.g. in a for loop), 2. they modify the argument. In your example you are using property 1. but not property 2., since you throw away the modified argument. If you don't need property 2. and just want the expression, then you can just write an expression, e.g. x+1 instead of x++.Lal
How on earth would you modify a + b without modifying a or b? Unless you intend ++(a+b) to mean "increment a AND increment b".Disparity
I think the point here is that while a+b MAY eventually be something that you can increment, there is no guarantee that there WILL be, as far as the language definition is concerned. And just as a side note, ++ is two characters, +1 is two characters. Don't be too OCD about it.Facsimile
I would have found this a much more interesting question if it had been about c = ++(a,b);Isosteric
@MrLister: How would that change the question in the slightest way?Nattie
@Olaf Well, because (a,b) is an expression that yields b. And b being an lvalue...Isosteric
@MrLister: That's true for the addition, too. But neverthless the comma-operator does not yield b, but it's value. See the standard, footnote 114!Nattie
P
119

It's just a rule, that's all, and is possibly there to (1) make it easier to write C compilers and (2) nobody has convinced the C standards committee to relax it.

Informally speaking you can only write ++foo if foo can appear on the left hand side of an assignment expression like foo = bar. Since you can't write a + b = bar, you can't write ++(a + b) either.

There's no real reason why a + b couldn't yield a temporary on which ++ can operate, and the result of that is the value of the expression ++(a + b).

Piggish answered 20/6, 2018 at 15:10 Comment(12)
I think point (1) hits the nail on the head. Just looking at the rules for temporary materialization in C++ can turn one's stomach (but it's powerful, though, gotta say that).Racy
@StoryTeller: Indeed, unlike our beloved language C++, C still compiles into assembly relatively trivially.Piggish
Bit of a shame that a pair of parentheses isn't interpreted as a nested operation. Then ++(a+b) could be translated into the equivalent of a+b | c = ++ Capers
Here's a real reason IMHO: it would be a dreadful confusion if ++ sometimes had a side effect of modifying something and sometimes just didn't.Adao
Foo& operator ++() {...} would allow OP's code to compile, whereas Foo& operator ++() & {...} would fail the way primitives do.Malayalam
@Malayalam although that would require your convincing the c standards committee of the need for references and operator overloading.Piggish
@Piggish I see, so it is a good idea to imagine 2 different classes of expressions/variable, left and right?Cullum
@dng: Indeed it is; that's why the terms lvalue and rvalue were introduced, although things are more complicated than that nowadays (particularly in C++). For example, a constant can never be an lvalue: something like 5 = a makes no sense.Piggish
@Piggish That explains why 5++ also causes a compilation errorCullum
@dng: Absolutely.Piggish
@Piggish And that is why the recommendation exists to put constants on the left in equality comparisons such as if( 5 == a ) so if there is a typo and one of the = is left out, the program will refuse to compile rather than silently set a equal to 5 instead of perform an equality check.Hardhearted
You can write (a+b)+1 if that was the intent, I think it would be detrimental to also add ++(a+b) with the same meaning: it adds nothing to the language, it violates the design principle of having there be only one way to do a thing, and it makes it harder for people to learn what ++ does since now there are two separate cases, ++lvalue and ++rvalueTrevor
H
41

The C11 standard states in section 6.5.3.1

The operand of the prefix increment or decrement operator shall have atomic, qualified, or unqualified real or pointer type, and shall be a modifiable lvalue

And "modifiable lvalue" is described in section 6.3.2.1 subsection 1

An lvalue is an expression (with an object type other than void) that potentially designates an object; if an lvalue does not designate an object when it is evaluated, the behavior is undefined. When an object is said to have a particular type, the type is specified by the lvalue used to designate the object. A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const-qualified type.

So (a+b) is not a modifiable lvalue and is therefore not eligible for the prefix increment operator.

Hardhearted answered 20/6, 2018 at 15:17 Comment(1)
Your conclusion from these definitions is missing... You want to say that (a+b) does not potentially designate an object, but these paragraphs do not allow that.Peoples
S
21

You are correct. the ++ tries to assign the new value to the original variable. So ++a will take the value of a, adds 1 to it and then assign it back to a. Since, as you said, (a+b) is a temp value, and not a variable with assigned memory address the assignment can't be performed.

Sublease answered 20/6, 2018 at 15:9 Comment(0)
R
12

I think you mostly answered your own question. I might make a small change to your phrasing and replace "temporary variable" with "rvalue" as C.Gibbons mentioned.

The terms variable, argument, temporary variable and so on will become more clear as you learn about C's memory model (this looks like a nice overview: https://www.geeksforgeeks.org/memory-layout-of-c-program/ ).

The term "rvalue" may seem opaque when you're just starting out, so I hope the following helps with developing an intuition about it.

Lvalue/rvalue are talking about the different sides of an equals sign (assignment operator): lvalue = left hand side (lowercase L, not a "one") rvalue = right hand side

Learning a little about how C uses memory (and registers) will be helpful for seeing why the distinction is important. In broad brush strokes, the compiler creates a list of machine language instructions that compute the result of an expression (the rvalue) and then puts that result somewhere (the lvalue). Imagine a compiler dealing with the following code fragment:

x = y * 3

In assembly pseudocode it might look something like this toy example:

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

The ++ operator (and its -- counterpart) need a "somewhere" to modify, essentially anything that can work as an lvalue.

Understanding the C memory model will be helpful because you'll get a better idea in your head about how arguments get passed to functions and (eventually) how to work with dynamic memory allocation, like the malloc() function. For similar reasons you might study some simple assembly programming at some point to get a better idea of what the compiler is doing. Also if you're using gcc, the -S option "Stop after the stage of compilation proper; do not assemble." can be interesting (though I'd recommend trying it on a small code fragment).

Just as an aside: The ++ instruction has been around since 1969 (though it started in C's predecessor, B):

(Ken Thompson's) observation (was) that the translation of ++x was smaller than that of x=x+1."

Following that wikipedia reference will take you to an interesting writeup by Dennis Ritchie (the "R" in "K&R C") on the history of the C language, linked here for convenience: http://www.bell-labs.com/usr/dmr/www/chist.html where you can search for "++".

Richellericher answered 20/6, 2018 at 16:40 Comment(0)
D
6

The reason is that the standard requires the operand being an lvalue. The expression (a+b) is not a lvalue, so applying the increment operator isn't allowed.

Now, one might say "OK, that's indeed the reason, but there is actually no *real* reason other than that", but unluckily the particular wording of how the operator works factually does require that to be the case.

The expression ++E is equivalent to (E+=1).

Obviously, you cannot write E += 1 if E isn't a lvalue. Which is a shame because one could just as well have said: "increments E by one" and be done. In that case, applying the operator on a non-lvalue would (in principle) be perfectly possible, at the expense of making the compiler slightly more complex.

Now, the definition could trivially be reworded (I think it isn't even originally C but an heirloom of B), but doing so would fundamentally change the language to something that's no longer compatible with its former versions. Since the possible benefit is rather small but the possible implications are huge, that never happened and probably is never going to happen.

If you consider C++ in addition to C (question is tagged C, but there was discussion about operator overloads), the story becomes even more complicated. In C, it's hard to imagine that this could be the case, but in C++ the result of (a+b) could very well be something that you cannot increment at all, or incrementing could have very considerable side effects (not just adding 1). The compiler must be able to cope with that, and diagnose problematic cases as they occur. On a lvalue, that's still kinda trivial to check. Not so for any kind of haphazard expression inside a parenthesis that you throw at the poor thing.
This isn't a real reason why it couldn't be done, but it sure lends as an explanation why the people who implemented this are not precisely ecstatic to add such a feature which promises very little benefit to very few people.

Dorpat answered 21/6, 2018 at 13:35 Comment(0)
S
3

(a+b) evaluates to an rvalue, which cannot be incremented.

Splasher answered 27/6, 2018 at 17:7 Comment(0)
F
3

++ tries to give the value to the original variable and since (a+b) is a temp value it cannot perform the operation. And they are basically rules of the C programming conventions to make the programming easy. That's it.

Fatidic answered 17/7, 2018 at 7:33 Comment(0)
E
2

When ++(a+b) expression performed, then for example :

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's invalid use of ++ operator 
*/
++(a+b);
Epiphora answered 26/6, 2018 at 6:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.