Live data from Hacker News

Dangerous Embedded C Coding Standard Rules (2011)

embeddedgurus.com

41–50 of 51 posts

Re: Dangerous Embedded C Coding Standard Rules (2011)

#41
post #30

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?

[deleted]

Re: Dangerous Embedded C Coding Standard Rules (2011)

#42
post #21

Earlier 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.

What a great comment from a longtime member of HN. Not only did you make a wonderful example of name-calling and an ad-hominem argument in a single sentence, you also failed to dispute the central argument which was that time is lost while translating the macro.

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)

#43
post #30
post #20

Well, 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?

It's not just division by 2. Most compilers will convert any division by a constant into either a shift (powers of 2) or a multiply (all other).

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)

#44
post #5

Even 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.

> 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)

#45
post #38

Earlier 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…

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;

Re: Dangerous Embedded C Coding Standard Rules (2011)

#46
post #45
post #38

Earlier 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;

That's fine until you have multiple lines that goto closefile or freestring, and you mix them up at some point, or you try to refactor the code and forget to update one of your gotos. The resulting bug will wait beneath the waters until one day you get a mysterious "out of file descriptors" or "out of memory" error at some random place in your program after it's been running for awhile.

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)

#47
post #2

Completely 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 isn't premature optimization, you're just guaranteeing that the compiler won't introduce the overhead of a function call."

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)

#48
post #26

I'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

That's the obvious next step, but it's not valid C89 and that may or may not be relevant for people doing embedded programming. Personally, I'm all for using newer standards and declaring variables locally when it makes sense. Sometimes I like to see all variables at the top of the function though.

Re: Dangerous Embedded C Coding Standard Rules (2011)

#49

I'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?

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.

Re: Dangerous Embedded C Coding Standard Rules (2011)

#50

Earlier 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.

Okay. I knew variables had block-level scope. I've read some of the ISO C standard, but hadn't caught the part where variables have function-level storage. I was wondering because, years ago, I read some disassembled code that did lots of stack allocations in the middle of functions, and not just for the purpose of calling another function.

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."

Post reply on HN