Live data from Hacker News

Dangerous Embedded C Coding Standard Rules (2011)

embeddedgurus.com

11–20 of 51 posts

Re: Dangerous Embedded C Coding Standard Rules (2011)

#11
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.

Depends on the ARM CPU. Some variants of ARMv7 architecture have an integer divide function.

https://en.wikipedia.org/wiki/ARM_architecture#Arithmetic_in...

Re: Dangerous Embedded C Coding Standard Rules (2011)

#12
post #7

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…

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. ...unless you know your compiler is capable of converting the division into cheaper operations via strength reduction, per your well-considered rule.

Another option is reciprocal multiplication, possibly with a larger integer type. Reciprocal multiplication is an important element of my software's ability to run a Kinect at ~30fps on an ARM cpu with no FPU and no division instruction.

Re: Dangerous Embedded C Coding Standard Rules (2011)

#13
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 way to detect whether a function has failed is to return an error code. The caller then has to test for the error code and take the appropriate action if there was an error. I'm working on a side project now that was doing a LOT of this because it does a lot of IO and memory allocation (and basically any operation you do on a file or any call to `malloc` or a function that calls `malloc` can spontaneously fail in C). 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) 
... and so on, and so forth. It got to the point where basically all of the function calls I did needed to be manually checked for an error, which made all of my code really messy and hard to read. The only real way to refactor these checks in pure C without sacrificing robustness is to use a macro:

    #define MY_ASSERT(c) do { if (!(c)) return ERROR_CODE; } while (0)
    MY_ASSERT((f = open_file()) != NULL);
    MY_ASSERT((s = allocate_string()) != NULL);
    MY_ASSERT(write_string_to_file(s, f) >= 0);
This has the same effect as the code above, without sacrificing comprehensibility or readability.

> Also macros are usually used to "speed up" the code. You should never optimize before finding the real bottleneck in your code. And thus always use functions instead of macros.

I think a lot of people on HN and /r/programming fail to understand why premature optimization is a bad thing. A lot of programmers hide behind the "premature optimization is the root of all evil" thing to justify their pre-existing biases or their inefficient code. The point is that you shouldn't spend a great deal of time optimizing functions or algorithms unless optimizing those functions or algorithms will be productive. It's wasteful to spend a bunch of time optimizing your IO operations, for example, if 95% of the time is being spent in the database. The macro-vs-function debate has nothing to do with that. Writing a macro definition takes no more effort than writing a function definition, so you shouldn't be opposed to writing

    #define SOME_OPERATION(x) (((x) * 100) / 3 + 500)
rather than

    int some_operation(int x) { return (x * 100) / 3 + 500; } 
That isn't premature optimization, you're just guaranteeing that the compiler won't introduce the overhead of a function call.

Re: Dangerous Embedded C Coding Standard Rules (2011)

#14
With regards to Bad Rule #3, "Avoid >= and use
    if (speed  99)
in code, my only response would be to question the author's sanity.

But then, I question the sanity of C standards committee members all the time, specifically regarding his actual complaint related to Bad Rule #2 and unspecified-width integer types[1].

[1] http://www.netrino.com/Embedded-Systems/How-To/C-Fixed-Width...

Re: Dangerous Embedded C Coding Standard Rules (2011)

#15
post #14

With regards to Bad Rule #3, "Avoid >= and use if (speed 99) in code, my only response would be to question the author's sanity. But then, I question the sanity of C standards committee members all the time, specifically regarding his actual complaint related to Bad Rule #2 and unspecified-width integer types[1]. [1] http://www.netrino.com/Embedded-Systems/How-To/C-Fixed-Width...

The phrase “C standards committee” is eminently ambiguous, as the standards that define the C language (e.g. ISO/IEC 9899) are the work of a committee. These committee members have constraints coming at them from all sides and do a very good job or maintaining compatibility with the past and future-proofing the C language at the same time.

This has nothing to do with coding standards, which any fool can define (and some have), and for which the only instance that really deserves the name of “standard” is MISRA-C. The other such “standards” have a scope typically limited to a company and its providers, and reflect the style and idiosyncrasies of the person who was in charge of defining it (they are rarely the work of a committee).

Re: Dangerous Embedded C Coding Standard Rules (2011)

#16
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…

I have to strongly disagree with your example. In fact I feel it may just be one of the worst usages of macros I have seen in that you are hiding the control flow of the program and thus obfuscating your code for other programmers. It might be easier for you to read because you wrote it but for every one that comes after you it will be a massive pain that adds unneeded additional cognitive overhead.

I say this from the point of view of someone having to debug your code: "OK so step, step, step, run to this line... wait, what? How did this function return when there is no return statement? Let me run that again from the beginning... ... ... What? This ASSERT macro doesn't actually assert it just returns?"

Keyboard out window etc.

Please stick to language conventions. Macros are bad because they hide what the code is actually doing.

Re: Dangerous Embedded C Coding Standard Rules (2011)

#17
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…

The first block of code seems more readable to me than the second. I'm used to seeing the first; it's idiomatic C. I don't understand why it says "do ... while (0)" in the macro, for instance. Wouldn't you want to handle different errors in different ways anyway? In your example, you're just going to leave the file open and the string allocated? Isn't trying to influence how the compiler compiles it by using a macro instead of a function a clear example of (premature) optimization?

Re: Dangerous Embedded C Coding Standard Rules (2011)

#18
use shift. This guy's a lefty clueless faggot.

My original compiler did yoda conditionals wrong. I assumed changing sides was negating logic. Not so.

I like #define. I don't like const and enum so I did not put them in my compiler. No typedef in my compiler, no ? operator.

This guy's just a retard.

God says... na_na you're_fired I'm_in_suspense rip_off what_a_mess little_fish geek you_do_it you_never_know mission_from_God after_a_break That's_my_favorite hopefully do_it Varoom youre_welcome cracks_me_up Mom bastard go_ahead_make_my_day fortitude Oh_Hell_No but_of_course anger quite segway wife so_let_it_be_done

Re: Dangerous Embedded C Coding Standard Rules (2011)

#19
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 speed at the expense of maintainability, and it's for the programmer to make intelligent trade offs, but the fact is that one method is faster than the other.

Silly example that decrements 'a' repeatedly if condition is non-zero:

   //temp = 0 inside if block
   int dec(int a, int condition) {
       int temp;

       if (condition) {
           temp = 0;  //
We can disassemble the output to verify that dec() will only write to memory if the condition was satisfied (see 400458), while dec2() will always write into temp (see 400482):

    # 
    400448:  push   %rbp
    400449:  mov    %rsp,%rbp
    40044c:  mov    %edi,0xffec(%rbp)
    40044f:  mov    %esi,0xffe8(%rbp)
    400452:  cmpl   $0x0,0xffe8(%rbp)  # if (condition)
    400456:  je     400473 
    400458:  movl   $0x0,0xfffc(%rbp)  # temp = 0   # while
    400461:  mov    0xfffc(%rbp),%eax
    400464:  sub    %eax,0xffec(%rbp)
    400467:  addl   $0x1,0xfffc(%rbp)
    40046b:  mov    0xfffc(%rbp),%eax
    40046e:  cmp    0xffec(%rbp),%eax
    400471:  jl     400461   # loop
    400473:  mov    0xffec(%rbp),%eax  # return a
    400476:  leaveq
    400477:  retq

    # 
    400478:  push   %rbp
    400479:  mov    %rsp,%rbp
    40047c:  mov    %edi,0xffec(%rbp)   
    40047f:  mov    %esi,0xffe8(%rbp)
    400482:  movl   $0x0,0xfffc(%rbp)   # temp = 0 
    40048f:  jmp    40049b   # while
    400491:  mov    0xfffc(%rbp),%eax
    400494:  sub    %eax,0xffec(%rbp)
    400497:  addl   $0x1,0xfffc(%rbp)
    40049b:  mov    0xfffc(%rbp),%eax
    40049e:  cmp    0xffec(%rbp),%eax
    4004a1:  jl     400491   # loop
    4004a3:  mov    0xffec(%rbp),%eax   # return a
    4004a6:  leaveq 
    4004a7:  retq
The above code was compiled with gcc 4.1.2 on amd64/linux. gcc -O2 and gcc -O3 completely do away with the 'temp' variable and generate fewer instructions.

Re: Dangerous Embedded C Coding Standard Rules (2011)

#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 rule is lunacy.

#4: Well, maybe. Except that variable initialization can often be made quite compact by the runtime (e.g., an expansion of a compressed data segment into RAM), and so save you code space.

#5: Again, the compiler can screw you here. Also, it's possible to take the address of a constant and have it be wiped out (for naïve implementations, anyway).

My basic rules:

#0: Don't make assumptions. Know what the heck is going on, fractally. Look at the generated code.

#1: Be a responsible engineer. Design and code for production and testing, and for the next guy. This often means not being clever, but if you are being clever, be forthright about it and don't leave your brilliance as a puzzle. (Don't be clever just to be clever. That will get you fired, no foolin').

#2: Avoid platitudes. Be wary of people who tell you not to use something, always, or to use a particular contruct, always. Be suspicious of code that follows someone else's dogma. [I once worked with a code base where the engineer had used the ternary -- "?:" -- operator everywhere because he thought it was more efficient than using 'if'. Of course it was just guesswork on his part and not only was his code a train wreck, but he had never measured his results. We fired that guy).

Embedded systems are just software, they're not magical, they're not (usually) that hard, and for some reason people are afraid of them. Some of my best moments in the industry have been writing these little things, they're fun as hell.

Post reply on HN