This problem came up in a anthor thread recently on avrfreaks. Rather than hijack it, I have started a new one on C coding style and perceived efficiencies. Please treat this post and any discussion it generates as a learning exercise.
The OP in the original thread asked for code to convert an array of 8 bits to a byte value - quite a simple task at first glance. I'm going to present three different solutions (2 from the thread and my own as well) and discuss each one. Here are the 3 sets of code in a single test case.
#includeint main(void) { unsigned char my_str[] ={0,0,0,0,0,1,1,1}; unsigned char my_char; volatile unsigned char i; /* first solution */ for (i = 0, my_char =0; i < sizeof(my_str); my_char <<= 1) my_char += my_str[i++]; //printf("%x\n", my_char); /* second solution */ for (i = 0; i < 8; i++) { my_char <<= 1; if (my_str[i]) my_char |= 0x01; } //printf("%x\n", my_char); /* my solution */ my_char = 0; for (i = 0; i < sizeof(my_str); i++) { my_char = (my_char * 2) + my_str[i]; } //printf("%x\n", my_char); }
If you compile and run this code with the MinGW C compiler on a PC, then the first solution will result in the answer 0xe instead of 0x7. Why - because the shift operation in the for loop is always executed one extra time. This code uses "side-effects" in a for loop and they can bite you.
If you compile this code with WinAVR then you get different results. Note I use WinAVR20060124 because it usually has the best optimization. I have not yet had a chance to execute this code. Here are the 3 sets of code, all 9 words long.
first solution: e8: 98 2f mov r25, r24 ea: f9 01 movw r30, r18 ec: 27 e0 ldi r18, 0x07 ; 7 ee: 81 91 ld r24, Z+ f0: 98 0f add r25, r24 f2: 21 50 subi r18, 0x01 ; 1 f4: 99 0f add r25, r25 f6: 27 ff sbrs r18, 7 f8: fa cf rjmp .-12 ; 0xeesecond solution: e8: f9 01 movw r30, r18 ea: 27 e0 ldi r18, 0x07 ; 7 ec: 99 0f add r25, r25 ee: 81 91 ld r24, Z+ f0: 81 11 cpse r24, r1 f2: 91 60 ori r25, 0x01 ; 1 f4: 21 50 subi r18, 0x01 ; 1 f6: 27 ff sbrs r18, 7 f8: f9 cf rjmp .-14 ; 0xec my solution: e8: 98 2f mov r25, r24 ea: f9 01 movw r30, r18 ec: 27 e0 ldi r18, 0x07 ; 7 ee: 99 0f add r25, r25 f0: 81 91 ld r24, Z+ f2: 98 0f add r25, r24 f4: 21 50 subi r18, 0x01 ; 1 f6: 27 ff sbrs r18, 7 f8: fa cf rjmp .-12 ; 0xee
So given code size and performance are equal, what separates these 3 solutions? Of course correctness of the answer, but also code readability and maintainability.
In my view first solution is hard to read and you need to think about what is happening to both the loop variable and the loop content at the same time because i and my_char are being changed in both. The answer on my PC is incorrect as well. I didn't test on an AVR yet but I suspect it is also incorrect.
The second solution uses a loop without side effects but then proceeds to use bit manipulations to create the correct result. This only works if the input array is using base 2 and in my view less readable because you have to figure out what the bits are doing. I know bit twiddling is part of AVR programming but it is unnecessary to make it any harder than it needs to be.
My solution uses a loop without side effects and a simple arithmetic expression to calculate my_char. This same code could be used with a different number base (e.g 8 or 10) by changing the number 2 to the correct base (or using a constant).
Some people try to "optimize" their C code thinking it is going to help. In many cases it actually doesn't. Modern compilers have very good optimization techniques and you should let the compiler do its job, rather than try to out guess them. I would humbly suggest that your "job" as a programmer is to produce correct, readable and maintainable code.
If something doesn't perform well, the first thing to look at is your data structures and algorithm. Usually a different choice results in faster code (c.f. bubblesort versus quicksort). This is optimization "in the large".
If you are tight on space then it might be time to go look at the code generated by the compiler and see if you can optimize something. This should be the path of last resort and is optimization "in the small".