Earlier quoted context omitted.
> #1: Avoiding shifting versus doing division: Nope. Crappy compilers and bad architectures will yield bad results. Know what you're doing. Which compiler, among those still used in 2014, does not convert a division by 2 into a shift?
Memory escapes me...is a shift guaranteed by the standard (and if so, which) to sign-extend or not sign-extend?
Dangerous Embedded C Coding Standard Rules (2011)
41–50 of 51 posts
Re: Dangerous Embedded C Coding Standard Rules (2011)
#42Earlier quoted context omitted.
That is almost exactly like some code I have written in a project and at that time it did it's job. After revisiting the project months later after I have completely forgotten the details, I got frustrated over the macro and what did it actually do( even though I have written it ). It took me some time to track down the macro definition and convert the code in my head to understand it again. But if the code would be…
"It took me some time to track down the macro definition " Right-click -> Go to definition? Your IDE must suck.
Apart from your already ( apparently )wast knowledge of various development tools, please also consider improving your conversational skills.
Re: Dangerous Embedded C Coding Standard Rules (2011)
#43Well, let's see. #1: Avoiding shifting versus doing division: Nope. Crappy compilers and bad architectures will yield bad results. Know what you're doing. #2: Use a typedef instead of a bare C type, then. Also, on many systems space is king (the last embedded system I wrote had 6 bytes left over in code space, and 16-bit operations were incredibly expensive and nearly unaffordable). #3: Just make the code clear. This…
> #1: Avoiding shifting versus doing division: Nope. Crappy compilers and bad architectures will yield bad results. Know what you're doing. Which compiler, among those still used in 2014, does not convert a division by 2 into a shift?
But there are lots of oddball compilers for CPUs, DSPs, and other embedded quasi processors you've never heard of. Many don't really optimise at all.
Re: Dangerous Embedded C Coding Standard Rules (2011)
#44Even better rule: Know the limitations of your target processor and compiler and code accordingly. Example 1: Some (bad) compilers just ignore the const keyword (and therefore treat consts like normal variables). Use #defines with these compilers (or get a better compiler). Example 2: Some (older, smaller) processors don't have an integer division instruction. Avoid division at all costs on these processors unless yo…
> Example 2: Some (older, smaller) processors don't have an integer division instruction. Avoid division at all costs on these processors unless you are not worried about space or execution speed. Ok, so steer clear of division on ARM CPUs.
Don't ignore the last part of the sentence:
> …unless you are not worried about space or execution speed.
I'd argue that on ARMs you're generally not worried about execution speed (as most run pretty fast nowadays), nor space (RAMs and flashes generally have lots of space).
Now, do you have a really slow ARM, or are you running in a really constrained space (like a tiny metal-mask ROM)? Then yes, avoid it.
Re: Dangerous Embedded C Coding Standard Rules (2011)
#45Earlier quoted context omitted.
> If you need macros to hack C to enable some functionality not inherent to the language, you should change the approach or switch to a different language. I think if you continue to do serious C hacking, you'll find a lot of places where macros are legitimately a good choice. For example, I've found that macros are very, very useful when it comes to implementing error handling in a robust way. In C, the only real wa…
So my source code, after a while, ended up looking more or less like this: if ((f = open_file()) == NULL) return ERROR_CODE; if ((s = allocate_string()) == NULL) return ERROR_CODE; if (write_string_to_file(s, f) I hope for you that's more 'less' than 'more'. That code leaks resources whenever an error occurs. Can't allocate the string? Oops, kept the file open. Write failed? Oops, kept the file open, and leaked the r…
int result = ERROR_CODE;
FILE * f = NULL;
char * s = NULL;
if((f = open_file()) == NULL) goto end;
if((s = allocate_string() == NULL) goto closefile;
if((some_other_thing() == NULL) goto freestring;
...
result = NOERROR;
freestring:
free_string( s);
closefile:
close_file( f);
return result;Re: Dangerous Embedded C Coding Standard Rules (2011)
#46Earlier quoted context omitted.
So my source code, after a while, ended up looking more or less like this: if ((f = open_file()) == NULL) return ERROR_CODE; if ((s = allocate_string()) == NULL) return ERROR_CODE; if (write_string_to_file(s, f) I hope for you that's more 'less' than 'more'. That code leaks resources whenever an error occurs. Can't allocate the string? Oops, kept the file open. Write failed? Oops, kept the file open, and leaked the r…
An advantage of using goto to handle errors is that you don't need that condition checking in the teardown: int result = ERROR_CODE; FILE * f = NULL; char * s = NULL; if((f = open_file()) == NULL) goto end; if((s = allocate_string() == NULL) goto closefile; if((some_other_thing() == NULL) goto freestring; ... result = NOERROR; freestring: free_string( s); closefile: close_file( f); return result;
And really, the extra condition check likely isn't going to affect the critical path, but it will make it a lot easier to reason about the code, especially when someone else has to modify it.
Re: Dangerous Embedded C Coding Standard Rules (2011)
#47Completely agree with no. 5. After a year with C I realized that macros are only to be used as control structures for headers and ifdefs to enable compatibility. If you need macros to hack C to enable some functionality not inherent to the language, you should change the approach or switch to a different language. Also macros are usually used to "speed up" the code. You should never optimize before finding the real b…
> If you need macros to hack C to enable some functionality not inherent to the language, you should change the approach or switch to a different language. I think if you continue to do serious C hacking, you'll find a lot of places where macros are legitimately a good choice. For example, I've found that macros are very, very useful when it comes to implementing error handling in a robust way. In C, the only real wa…
That's precisely what premature optimization is: Reducing assumed overhead before you've even established where the bottleneck is, if any. By using a macro, you've traded away all of the benefits of a function. If you had demonstrable need to avoid the function call overhead, you could have declared the function inline static.
Most performance issues can be solved with algorithmic changes. This level of optimization should only be done as a last resort, and profiled extensively to ensure that you really are making a difference for the better. Many "low level" tricks that on the surface look like they'd speed things up can in fact have the opposite effect on modern processors. For example, it's possible that this macro optimization could blow up your cache if some_operation gets called a lot and you tried unrolling loops.
Re: Dangerous Embedded C Coding Standard Rules (2011)
#48I'm not completely on board with #4 (initialization of variables). I agree that 'declaring and then assigning a value' _within the same block_ vs 'initialized declaration' has no speed gains, only downsides. However, if the assignment can happen in a _different block_ (maybe inside an 'if' block) you could save 1 memory write, depending on how many times the if condition is satisfied. This obviously optimizes for spe…
I'd suggest that, if a variable with an undefined value is a valid state for the program, the declaration is in the wrong place. How about this alternative? int dec(int a, int condition) { if (condition) { int temp = 0; //compute something here in a loop while (temp
Re: Dangerous Embedded C Coding Standard Rules (2011)
#49I'm not completely on board with #4 (initialization of variables). I agree that 'declaring and then assigning a value' _within the same block_ vs 'initialized declaration' has no speed gains, only downsides. However, if the assignment can happen in a _different block_ (maybe inside an 'if' block) you could save 1 memory write, depending on how many times the if condition is satisfied. This obviously optimizes for spe…
I'm not particularly familiar with x86-64 calling conventions. Where is the code that sets up the stack frame? Would this code look different if temp were created inside the if() block, not just initialized there?
Re: Dangerous Embedded C Coding Standard Rules (2011)
#50Earlier quoted context omitted.
I'm not particularly familiar with x86-64 calling conventions. Where is the code that sets up the stack frame? Would this code look different if temp were created inside the if() block, not just initialized there?
In C, variables have function-level storage. I don't recall the actual term from the C standard, but the storage for _all_ variables declared in any blocks inside a function is allocated at the beginning of the function, when the stack frame is set up. So there is 'no way' to create 'temp' only inside the if block.
Can you point to a reference showing that variables have function-level storage? The closest I can find in the C standard draft I'm looking at is 6.2.4.6, which suggests block-level lifetimes for variables: "For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way."