4

What is the real advantage of using compound assignment in C/C++ (or may be applicable to many other programming languages as well)?

#include <stdio.h>

int main()
{
    int exp1=20;
    int b=10;
   // exp1=exp1+b;
    exp1+=b;

    return 0;
};

I looked at few links like microsoft site, SO post1, SO Post2 . But the advantage says exp1 is evaluated only once in case of compound statement. How exp1 is really evaluated twice in first case? I understand that current value of exp1 is read first and then new value is added. Updated value is written back to the same location. How this really happens at lower level in case of compound statement? I tried to compare assembly code of two cases, but I did not see any difference between them.

Rajesh
  • 1,085
  • 1
  • 12
  • 25
  • There's a good chance that your compiler optimised it. Anyway, assembly language usually has increment opcodes. In fact, they don't have anything else. If you do something like `1 + 2` in C, it would be compiled to something like `move 1,a` and `add 2,a`. – SeverityOne Apr 19 '18 at 13:22
  • @SeverityOne's response should be the answer... – Frederick Apr 19 '18 at 13:27

6 Answers6

3

For simple expressions involving ordinary variables, the difference between

a = a + b;

and

a += b;

is syntactical only. The two expressions will behave exactly the same, and might well generate identical assembly code. (You're right; in this case it doesn't even make much sense to ask whether a is evaluated once or twice.)

Where it gets interesting is when the left-hand side of the assignment is an expression involving side effects. So if you have something like

*p++ = *p++ + 1;

versus

*p++ += 1;

it makes much more of a difference! The former tries to increment p twice (and is therefore undefined). But the latter evaluates p++ precisely once, and is well-defined.

As others have mentioned, there are also advantages of notational convenience and readability. If you have

variable1->field2[variable1->field3] = variable1->field2[variable2->field3] + 2;

it can be hard to spot the bug. But if you use

variable1->field2[variable1->field3] += 2;

it's impossible to even have that bug, and a later reader doesn't have to scrutinize the terms to rule out the possibility.

A minor advantage is that it can save you a pair of parentheses (or from a bug if you leave those parentheses out). Consider:

x *= i + 1;         /* straightforward */
x = x * (i + 1);    /* longwinded */
x = x * i + 1;      /* buggy */

Finally (thanks to Jens Gustedt for reminding me of this), we have to go back and think a little more carefully about what we meant when we said "Where it gets interesting is when the left-hand side of the assignment is an expression involving side effects." Normally, we think of modifications as being side effects, and accesses as being "free". But for variables qualified as volatile (or, in C11, as _Atomic), an access counts as an interesting side effect, too. So if variable a has one of those qualifiers, a = a + b is not a "simple expression involving ordinary variables", and it may not be so identical to a += b, after all.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • Got the real issue now. I think most books does not highlight this concept of two time evaluation by giving good example. In fact this is the most important difference than the readability. @dbush also gave another good example where we can easily see the result of two time evaluation. Disadvantage indicated by chux also give us better clarity. – Rajesh Apr 19 '18 at 14:58
  • No, they are not always the same. It depends on what `a` is. If it is `volatile` or `_Atomic` the result is fundamentally different. – Jens Gustedt Apr 19 '18 at 18:54
  • How would tmp = *XYZ; be interpreted and why? my compiler interprets this as: Copy *XYZ to temp, and why not tmp = tmp * XYZ ? Note: XYZ is pointer. – M Sharath Hegde Aug 30 '18 at 06:58
  • 1
    @MSharathHegde *If* the compound assignment operator were spelled "`=*`", there might be some ambiguity here, but since it is in fact spelled "`*=`", it's perfectly clear that `tmp=*XYZ` must involve a pointer access, not a multiplication. (Now, in the earliest days of C, the compound assignment operator *was* spelled "`=*`", and there *was* ambiguity, which is why the compound assignment operators were rejiggered to their modern form.) – Steve Summit Aug 30 '18 at 09:27
2

Evaluating the left side once can save you a lot if it's more than a simple variable name. For example:

int x[5] = { 1, 2, 3, 4, 5 };
x[some_long_running_function()] += 5;

In this case some_long_running_function() is only called once. This differs from:

x[some_long_running_function()] = x[some_long_running_function()] + 5;

Which calls the function twice.

dbush
  • 205,898
  • 23
  • 218
  • 273
2

This is what the standard 6.5.16.2 says:

A compound assignment of the form E1 op= E2 is equivalent to the simple assignment expression E1 = E1 op (E2), except that the lvalue E1 is evaluated only once

So the "evaluated once" is the difference. This mostly matters in embedded systems where you have volatile qualifiers and don't want to read a hardware register several times, as that could cause unwanted side-effects.

That's not really possible to reproduce here on SO, so instead here's an artificial example to demonstrate why multiple evaluations could lead to different program behavior:

#include <string.h>
#include <stdio.h>

typedef enum { SIMPLE, COMPOUND } assignment_t;

int index;

int get_index (void)
{
  return index++;
}

void assignment (int arr[3], assignment_t type)
{
  if(type == COMPOUND)
  {
    arr[get_index()] += 1;
  }
  else
  {
    arr[get_index()] = arr[get_index()] + 1;
  }
}

int main (void)
{
  int arr[3];

  for(int i=0; i<3; i++) // init to 0 1 2
  {
    arr[i] = i;
  }
  index = 0;
  assignment(arr, COMPOUND);
  printf("%d %d %d\n", arr[0], arr[1], arr[2]);   // 1 1 2

  for(int i=0; i<3; i++) // init to 0 1 2
  {
    arr[i] = i;
  }
  index = 0;
  assignment(arr, SIMPLE);
  printf("%d %d %d\n", arr[0], arr[1], arr[2]);   // 2 1 2 or 0 1 2
}

The simple assignment version did not only give a different result, it also introduced unspecified behavior in the code, so that two different results are possible depending on the compiler.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
Lundin
  • 195,001
  • 40
  • 254
  • 396
2

Advantage of using compound assignment

There is a disadvantage too.
Consider the effect of types.

long long exp1 = 20;
int b=INT_MAX;

// All additions use `long long` math
exp1 = exp1 + 10 + b;

10 + b addition below will use int math and overflow (undefined behavior)

exp1 += 10 + b;  // UB 
// That is like the below,
exp1 = (10 + b) + exp1;
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

Not sure what you're after. Compound assignment is shorter, and therefore simpler (less complex) than using regular operations.

Consider this:

player->geometry.origin.position.x += dt * player->speed;

versus:

player->geometry.origin.position.x = player->geometry.origin.position.x + dt * player->speed;

Which one is easier to read and understand, and verify?

This, to me, is a very very real advantage, and is just as true regardless of semantic details like how many times something is evaluated.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • Arguably the main issue with your example is that you don't follow the law of Demeter. The difference between `x += dt * speed` and `x =x + dt * speed` is less, but still worth having. – Pete Kirkham Apr 19 '18 at 13:38
  • I agree with this answer. Modern compilers usually do not need any "help" by programmers using specific language constructs. In fact today better readability of a code fragment usually also means better optimization by the compiler. – Blue Apr 19 '18 at 13:41
  • @unwind I am mainly talking about performance related issues rather than readability. According to Microsoft, "However, the compound-assignment expression is not equivalent to the expanded version because the compound-assignment expression evaluates expression1 only once, while the expanded version evaluates expression1 twice: in the addition operation and in the assignment operation". Here is what I am expecting some kind of explanation. – Rajesh Apr 19 '18 at 13:49
0

A language like C is always going to be an abstraction of the underlying machine opcodes. In the case of addition, the compiler would first move the left operand into the accumulator, and add the right operand to it. Something like this (pseudo-assembler code):

move 1,a
add 2,a

This is what 1+2 would compile to in assembler. Obviously, this is perhaps over-simplified, but you get the idea.

Also, compiler tend to optimise your code, so exp1=exp1+b would very likely compile to the same opcodes as exp1+=b.

And, as @unwind remarked, the compound statement is a lot more readable.

SeverityOne
  • 2,476
  • 12
  • 25
  • I am using TDM-GCC-64 compiler. Optimization is turned off. Here is the log 'gcc.exe -Wall -s -pedantic -Wextra -Wall -g -c "C:\sample_Project_Only_Main\main.c" -o Debug\main.o g++.exe -o Debug\sample_Project_Only_Main.exe Debug\main.o " – Rajesh Apr 19 '18 at 13:54
  • Fair enough. Still, assembly always works along the lines of "add something to a register or memory location", which is what the `+=` operator does. – SeverityOne Apr 19 '18 at 17:03