3

As I say above, is it bad practice? In ASM how it will look like? I mean, I don't know if it is translated to something like this:

arr[0] = value;
arr[1] = value;

Or to something like this:

arr[1] = value;
arr[0] = arr[1];

Which the second one is obviously more inefficient (imm vs mem).

Thanks in advance.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 3
    Well, I guess, any good compiler would optimize that away. And in (x86) assembler, it's more of a "cache vs. dependency chain" thing. – zx485 May 04 '20 at 18:52
  • 1
    Compilers are very good at optimising this kind of thing. You shouldn't worry too much about the performance here as the compiler is likely to generate the same code from both if optimisations are enabled. Note that most C compilers provide a way to inspect their assembly output in case you are interested in that. – fuz May 04 '20 at 19:01
  • 2
    Just do the former. Readable and efficient. What's the point of the latter? Less readable, and no more efficient. – Erik Eidt May 04 '20 at 19:03
  • the latter conforms to the language and the former doesnt although optimization bypasses intermediate values regularly. If the variables are global then there is a timing assumption and I would hope it does the latter. – old_timer May 04 '20 at 20:21

3 Answers3

7

According to the C grammar assignment operators evaluate from right to left.

So this statement

arr[0] = arr[1] = value;

is equivalent to

arr[0] = ( arr[1] = value );

From the C Standard (6.5.16 Assignment operators)

3 An assignment operator stores a value in the object designated by the left operand. An assignment expression has the value of the left operand after the assignment,111) but is not an lvalue. The type of an assignment expression is the type the left operand would have after lvalue conversion. The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. The evaluations of the operands are unsequenced.

So according to the quote you may consider this statement

arr[0] = arr[1] = value;

like

arr[1] = value;
arr[0] = arr[1];

As for efficiency then the compiler can generate an efficient object code that will differ from what you imagine seeing a source code.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

Well, I looked at the assembly code and I got this:

182             Arr[0] = 0x01;
e357:   A601 LDA #0x01
e359:   C70100 STA 0x0100
184             Arr[0] = Arr[1] = 0x58;
e35d:   A658 LDA #0x58
e35f:   C70101 STA 0x0101
e362:   C70100 STA 0x0100

So the result expected if I do Arr[0] = 0x58; and immediately Arr[1] = 0x58; is:

e357:   A601 LDA #0x58
e359:   C70100 STA 0x0100
e359:   C70100 STA 0x0101

(STA: STore Accumulator | LDA: LoaD Accumulator)

So, the compiler I'm using optimizes the code. And you people have spoken, I assume better practice (or a better readable way) using:

Arr[0] = 0x58; 
Arr[1] = 0x58;

Thanks to all!

  • If your constant is just a numeric literal, you might want to avoid repeating it by writing `Arr[0] = Arr[1] = ...`. Otherwise yes, repeating some meaningful symbolic name for the value is generally fine. The only thing you should really avoid for good style is having `Arr[1] = Arr[0];` as a separate statement on a separate line. *Then* you should certainly have used `value` again. – Peter Cordes May 05 '20 at 02:50
0

If you want to see the asm you can use

objdump -d -M intel binary_name

Then you'll search for the .text section and then the main function.


Here is what I got on my computer using int arr[2] and a simple printf to use the variables.

Optimized output, compiled with -O3

mov    ecx,0x2a
mov    edx,0x2a

Not optimized

For this code:

arr[0] = 42;
arr[1] = 42;

The output is:

mov    DWORD PTR [rbp-0x10],0x2a
mov    DWORD PTR [rbp-0xc],0x2a

And for this code:

arr[0] = arr[1] = 42;

The output is:

mov    DWORD PTR [rbp-0xc],0x2a
mov    eax,DWORD PTR [rbp-0xc]
mov    DWORD PTR [rbp-0x10],eax

In the second case there is an additional operation.

So with an optimized compilation, there is no difference, but for code readability I would not write it this way.

Mickael B.
  • 4,755
  • 4
  • 24
  • 48
  • I'm surprised that there's a difference in the two, particularly if you have optimizations turned on. – Russ Schultz May 04 '20 at 19:57
  • @RussSchultz there is a difference when it's not optimized, I've updated with optimized output, it is the same. – Mickael B. May 05 '20 at 01:12
  • @RussSchultz: you can see from use of `[rbp - ...]` for locals (`-fno-omit-frame-pointer`), and the reload of the just-stored value, that this is un-optimized (anti-optimized) debug mode code. [Why does clang produce inefficient asm with -O0 (for this simple floating point sum)?](https://stackoverflow.com/q/53366394) applies to basically all compilers. Some compilers might have optimized even within that one statement `a=b=42` statement in debug mode, but this compiler didn't. The optimized code optimized away the array entirely and only materialized the value as register args for printf. – Peter Cordes May 05 '20 at 02:45
  • Fun fact: `mov ecx,edx` would actually be a good choice in this case to save some code-size (2 bytes vs. 5-byte `mov r32, imm32`). But compilers don't know that `printf` is slow and won't get to its 4th arg (ECX) for some time, and a 1 cycle dependency would be trivial anyway. – Peter Cordes May 05 '20 at 02:47