Академический Документы
Профессиональный Документы
Культура Документы
Page 1 of 23
Home
Home
Bloggers
Posts by Category
Newsletter Signup
About Us
Contact Us
Log in
Search for:
Search Blog
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 2 of 23
In principle, I agree with the underlying practices of not always declaring variables int and choosing the type (and
signedness) based on the maximum range of values. However, I think it essential that any practice like this be matched with a
corresponding practice of always declaring specifically sized variables using C99s portable fixed-width integer types.
It is impossible to understand the reasoning of the original programmer from unsigned char seconds;. Did he choose char
because it is big enough or for some other reason? (Remember too that a plain char may be naturally signed or unsigned,
depending on the compiler. Perhaps the original programmer even knows his compilers chars are default unsigned and
omits that keyword.) The intent behind variables declared short and long is at least as difficult to decipher. A short integer
may be 16-bits or 32-bits (or something else), depending on the compiler; a width the original programmer may have (or may
not have) relied upon.
Better Rule: Whenever the width of an integer matters, use C99s portable fixed-width integer types.
A variable declared uint16_t leaves no doubt about the original intent as it is very clearly meant to be a container for an
unsigned integer value no wider than 16-bits. This type selection adds new and useful information to the source code and
makes programs both more readable and more portable. Now that C99 has standardized the names of fixed-width integer
types, declarations involving short and long should no longer be used. Even char should only be used for actual character
(i.e., ASCII) data. (Of course, there may still be int variables around, where size does not matter, such as in loop counters.)
Bad Rule #3: Avoid >= and use <.
As worded above, I cant say I understand this rule or its goal sufficiently, but to illustrate it BadAdvice gives the specific
example of an if-else if wherein he recommends if (speed < 100) ... else if (speed > 99) instead of if (speed <
100) ... else if (speed >= 100). Say what? First of all, why not just use else for that specific scenario, as speed must
be either below 100 or 100 or above.
Even if we assume we need to test for less than 100 first and then for greater than or equal to 100 second, why would anyone
in their right mind prefer to use greater than 99? That would be confusing to any reader of the code. To me it reads like a bug
and I need to keep going back over it to find the logical problem with the apparently mismatched range checks. Additionally, I
believe that BadAdvices terse rationale that Benefits: Lesser Code is simply untrue. Any half decent compiler should be
able to optimize either comparison as needed for the underlying processor.
Better Rule: Use whatever comparison operator is easiest to read in a given situation.
One of the very best things any embedded programmer can do is to make their code as readable as possible to as broad an
audience as possible. That way another programmer who needs to modify your code, a peer doing code review to help you
find bugs, or even you years later, will find the code hard to misinterpret.
Bad Rule #4: Avoid variable initialization while defining.
BadAdvice says that following the above rule will make initialization faster. He gives the example of unsigned char
MyVariable = 100; (not preferred) vs:
#define INITIAL_VALUE 100
unsigned char MyVariable;
// Before entering forever loop in main
MyVariable = INITIAL_VALUE
Though its unclear from the above, lets assume that MyVariable is a local stack variable. (It could also be global, the way
his pseudo code is written.) I dont think there should be a (portably) noticeable efficiency gain from switching to the latter.
And I do think that following this rule creates an opening to forget to do the initialization or to unintentionally place the
initialization code within a conditional clause.
Better Rule: Initialize every variable as soon as you know the initial value.
Id much rather see every variable initialized on creation with perhaps the creation of the variable postponed as long as
possible. If youre using a C99 or C++ compiler, you can declare a variable anywhere within the body of a function.
Bad Rule #5: Use #defines for constant numbers.
The example given for this rule is of defining three constant values, including #define ON 1 and #define OFF 0. The
rationale is Increased convenience of changing values in a single place for the whole file. Provides structure to the code.
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 3 of 23
And I agree that using named constants instead of magic numbers elsewhere in the code is a valuable practice. However, I
think there is an even better way to go about this.
Better Rule: Declare constants using const or enum.
Cs const keyword can be used to declare a variable of any type as unable to be changed at run-time. This is a preferable way
of declaring constants, as they are in this way given a type that can be used to make comparisons properly and enabling them
to be type-checked by the compiler if they are passed as parameters to function calls. Enumeration sets may be used instead
for integer constants that come in groups, such as enum { OFF = 0, ON };.
Final Thoughts
There are two scary things about these and a few of the other rules on BadAdvices blog. First, is that they are out there on the
Internet to be found with a search for embedded C coding rules. Second, is that BadAdvices bio says he works on medical
device design. Im not sure which is worse. But I do hope the above reasoning and proposed better rules gets you thinking
about how to develop more reliable embedded software with fewer bugs.
Tags: bugs, embedded, firmware, programming, safety, standards
This entry was posted on Tuesday, August 30th, 2011 at 2:13 pm and is filed under Coding Standards, Efficient C/C++, Firmware Bugs. You can follow
any responses to this entry through the RSS 2.0 feed. You can skip to the end and leave a response. Pinging is currently not allowed.
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 4 of 23
as well as performance. Most compilers _are_ smart enough to do constant folding so that even if the constant has
to go into SRAM, only one instance will be created (albeit perhaps only in module-scope) depending on the
toolchain.
For uses of consts as constant symbols in module scope that are declared and not used, the first reservation is a
non-issue and isnt the worst practice. In my opinion, enums are fine solution iff you fully intent to make full use
of the type definition that comes out of it, and dont simply use it as a symbolic constant that gets slopped-around
between variables of other types (chars, uintN_t, etc.).
I havent checked but my memory of enums was that theyre int-sized, which has all of the space-penalty
liabilities for those of us that work on resource-constrained systems, if my target requires only 8/16 bits.
About your reservations regarding BadRule2, why not just use typedefs rather than #defines to establish the C99
types? By not using typedefs you dont get any of the strong-typing benefits that Michael rightly wants to
perpetuate, whereas #defining them merely maps them to their weakly-specified types (int, long, etc.) without
almost none of the benefits.
The other comment is that most compilers are also smart enough to be able to identify the many of the worst
#defined constructions at their point of (mis-)use in particular, constants known to be assigned inappropriate
lvalues (signed where unsigned is required, or stores into datatypes are too small for the #defined rvalue). Maybe
not in all cases, but in many. Id love to know about common counter-examples, although only reasonable, nontoy compilers need apply
As far as BadRule1 and BadRule3 are concerned, I cant help but say The 1980s called and they want their
compilers back. Unless one can definitively otherwise, the compiler *is* smarter than you are. These two
rules are clearly dead-fish waving.
Reply
2. Carle Henson says:
August 30, 2011 at 4:20 pm
I think whoever wrote rule 1 must be living in the far distant past when compilers were not intelligent in terms of
optimization and we had to wring every last cycle out of the code. Perhaps there are still severely limited processors
with outdated compilers but all of the compilers I have used in many years would do the shift optimization for you if it
can.
Reply
hpro says:
September 15, 2011 at 12:43 pm
It actually gets better. The compiler will do *better* division than you can do manually.
Check out http://ridiculousfish.com/blog/archives/2010/02/15/labor-of-division-episode-i/index.html for a good
example.
Modern compilers are so good, that in quite a lot of cases you will cause the compiler to generate *worse* code
because it is bending over backwards to jump through your optimization hoops.
Reply
3. Eric Smith says:
August 30, 2011 at 4:30 pm
I think an even better rule than always using the C standards fixed-width integer types is to use the standards minimum
-width integer types where the memory usage isnt critical. In other words, for a value from 0 to 10000 that is going to
be in a data structure that has a lot of instances, Id use uint16_t, but where the size is less important, Id use
uint_least16_t. This gives the compiler the flexibility to use a wider integer if it wants to, possibly for optimization. For
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 5 of 23
example, on some 32-bit processors, using uint16_t might force the compiler to add masking instructions, while it might
be more efficient to use 32-bit storage with no masking, and uint_least16_t allows that.
For code where maximum performance is essential, perhaps in an inner loop, Id go one step further and use
uint_fast16_t, which tells the compiler both that the width must be at least 16, and that I want whatever type will make
the code fastest.
Reply
Thomas Thorsen says:
August 31, 2011 at 2:44 pm
Actually you should use uint_fastXX_t to avoid masking instructions. For example, on a 32bit ARM processor,
uint_least16_t will be a 16bit type and subject to masking instructions following arithmetic operations on the
register that contains the value.
I think it is fair to say that the uint_leastXX_t types are quite useless on any platform that provides the full set of
uintXX_t types, as they are required by the standard to be identical types in this situation. Note that the uintXX_t
types are not mandatory in C99, but i doubt anyone would consider not providing them!
Reply
David Brown says:
August 31, 2011 at 4:18 pm
There are some processors (particularly DSPs) that cant access 8-bit, or perhaps even 16-bit data. On such
targets, a char is 16-bit or 32-bit, which is still valid for C. Then there is no uint8_t and perhaps no
uint16_t either but uint_least8_t is still there. So if you want to write code that will work on such a DSP
and will use the smallest type possible for better code on other cpus, you use uint_least8_t, etc.
Reply
4. Hans Van Ingelgom says:
August 30, 2011 at 4:44 pm
Another bad idea in my opinion is using enums for bit fields, like typedef enum { blinking=1, bold = 2, inverted = 4 }
fonttype; If you define an enumerated type, you are saying there is a type of which the values can be enumerated (duh!).
If you then say fonttype t = bold | blinking, the value of t is not a member of fonttype. Ive seen quite a lot of these
constructs, however I always think its wrong. Or is it just me? Also if I remember correctly, C++ makes a much bigger
fuzz about this.
Reply
Krzysztof Wesoowski says:
August 30, 2011 at 5:07 pm
C++ allows more elegant approach see http://doc.qt.nokia.com/latest/qflags.html.
Reply
scott says:
August 30, 2011 at 6:03 pm
i actually dont have a problem with this, at least if there is sufficient documentation near the definition of
fonttype that indicates that the enum is a bit field. although when i read the assigment fonttype t = bold | blinking;
i cant help but to think that t is bold *or* blinking instead of bold *and* blinking.
kinda wish there was a key word that enforced it like the [Flags] attribute in c#.
Reply
Eric Wertz says:
August 31, 2011 at 8:17 pm
I cant condone this. If you believe at all in what the abstraction of what an enum is supposed to be, using
an enum in a context where bitwise operations are desired, is an abomination. In the purest sense, one
shouldnt even know what the _range_ of values are in the enumeration. However, we all know that being
able to assign them concrete values has so much value in the real world that the language has conceded to
break the most-pure abstraction of what an enumeration is.
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 6 of 23
Bitwise operations are intended for uints, or bitfields if you really the syntactic sugar in your coding diet.
If you dont trust yourself to use | and & correctly, then you should be sticking to bitfields and stick
with the logical operators. This just isnt the right time or place for an enumerated type. If youre really
committed to writing bug-free code, one needs to recognize constructions that both the reader (and writer!)
of the code get hung up on, and steer clear of them.
Reply
Ashleigh says:
August 30, 2011 at 6:33 pm
Agreed enums should not be used for bitfields. Although you can have enums where the enumerants have any
value you care to assign (just a named number) I find it more helpful to think of then as a whole new type which
is not compatible with addition or logical operators.
So things like bitfields need to use either a struct with bit field parts in, or #defines.
Bad rule #5 should be considered alonside the compiler. If the compiler generates bigger code or larger constant
sections when declaring const then there may be good reason to use #define. In general though, the advice to
use const is better the compiler does more work.
Reply
5. Alex Converse says:
August 30, 2011 at 5:01 pm
As far as rule 5 goes, Ive been bitten by using const int instead of a define.
#define SLOTS 10
int my_array[SLOTS]; /* Standard C array */
const int slots = 10;
int my_array[slots]; /* Technically this is a Variable Length Array */
Reply
Gauthier says:
August 31, 2011 at 1:14 am
+1, and the following cannot be done with const:
#define TICK_FREQUENCY (50U)
#define SECONDS_IN_PERIOD (60U)
#define TICKS_IN_PERIOD (TICK_FREQUENCY * SECONDS_IN_PERIOD)
Furthermore if the const/define value is to be used in several modules, where is the const supposed to be defined?
In my opinion it must be in the c file, and declared as extern in the h file. This forces you to touch the c file if the
variable needs changing. Const takes also memory (although little), which define does not.
On the other hand, const are easier to debug.
Reply
6. David says:
August 30, 2011 at 5:22 pm
how to develop more reliable embedded software with fewer bugs.?
Allow peers to review it: http://youtu.be/nFZGpES-St8?t=5m
Reply
7. Ark says:
August 30, 2011 at 5:49 pm
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 7 of 23
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 8 of 23
Reply
11. Trenton says:
August 30, 2011 at 9:30 pm
Never shift when you mean to divide. If you really are convinced that you are better at optimizing than your compiler
use a macro like divide_x_by_32(x) and hide your shifting inside there. Also, shifting doesnt work for products or
quotients if the number being shifted is negative. Not all CPUs have single cycle shifts, some have single cycle divides,
etc. So using a macro makes it easier to port.
One reason to choose not to initialize variable when declared is to alter the time when initialization takes place.
Initialized variables are usually initialized at reset, before main(). Uninitialized variables assigned later in the code are
usually initialized when the core executes. This amortizes the time to initialize variables.
Using const variables instead of #defines occupies storage, which may or may not be what you want. You need to be
careful when using enums since many compiler treat then as signed integers, which may not be what you wanted. For
example, passing narro enums as wide arguments may cause the compiler to add code to sign extend them.
Reply
david collier says:
August 31, 2011 at 6:31 am
um, doesnt a #define specify a sequence of characters. so compilers dont treat them as signed anything, they put
the characters in the matching place, and they get treated as if they belong there. Shouldnt you be able to get the
desired effect by specifying the literal value fully?
Reply
Trenton says:
August 31, 2011 at 7:40 am
First, what I wrote about variable initialization is incorrect with respect to automatic variables in a function
invocation.
Second, what I meant about signed numbers in shifts is that -4 >> 1 doesnt equal -2.
If you use a #define to create a constant, say @define myvar -5, and then you pass that as a parameter to a
function, say f(uint32_t x) in a call like f(myvar) then the preprocessor replaces f(myvar) by f(-5) before the
compiler sees it. In this case you may get what you wanted.
If you make an enum { one = 1, two, three} then usually that will be treated as a signed value of some fixed
size (perhaps int) or a signed value of the smallest size big enough to represent the value; how this is done is
compiler dependent and may or may not be under programmer control. Assuming that the enum becomes an
int, if you then call f(long x) as f(one) then the compiler will convert the enum-as-int to a long. The point
was that passing enumerated values as parameters may cause the compiler to inject code to massage them
on the way into the function.
Reply
david collier says:
September 2, 2011 at 6:48 am
as discessed elsewhere on thsi page
Second, what I meant about signed numbers in shifts is that -4 >> 1 doesnt equal -2.
is incorrect. It may, or it may not, its implementation dependent.
Reply
12. Bill Davy says:
August 31, 2011 at 2:11 am
Rule 3 may also have the undesirable tendency to introduce another constant (99 in your example) requirng either an
expression using a constant (MaximumValue 1) ora new cosnat to be (defined ?) MaximumLessOne.
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 9 of 23
Rule 4. One reason for using #define is when a compiler cannot use conts variables to work out an array size. best to
avoid such puerile compilers but sometimes one has to. Also, for a bit field widths, a literal seems to be required. But far
better as a rule, have no literal constants in your code use const (best) or #define (if required).
Reply
13. Tomasz says:
August 31, 2011 at 2:15 am
@Bad Rule #5
IMHO declaring a constant as #define has its advantages in many cases, e.g. when one cant afford to waste too much
RAM. One other reason in small and slow environments, i.e. 8 bit uCs, it makes sense to load an immediate value into
a variable, since its faster than loading one const to a register and only then copying it to another variable. There are
times when I have to do such optimization due to sb-elses poor hardware design (e.g. a system that was meant to do
sth very simple now has to do more complex tasks on the same machine), although I agree it needs extra care from my
side. This is a particular situation, because I always know that the code is not going to be maintained by anybody else.
Reply
David Brown says:
August 31, 2011 at 2:28 pm
If you declare something as const int two = 2; at file scope, it has external linkage. That means the compiler has
to assume that anyone can change its value (yes, const variables are variables not true constants) from other
modules that the compiler cannot see at compile time. So the constant two gets allocated space in ram (or
sometimes flash), and this space must be read when you use it. And x = x / two; means using a division
instruction or library call.
The correct way to define such constants is static const two = 2; then the compiler knows exactly when and
how two is used, and can see that it is never changed. It can then substitute the literal value 2 for two
throughout the code, and x = x / two; becomes a right-shift operation.
Reply
14. Konrad says:
August 31, 2011 at 2:59 am
Re Bad Rule 1:
The important thing to remember about using right shifts for division is that it rounds the quotient of a signed dividend
towards minus infinity. -1 >> 1 is -1, not 0.
I suspect (as the original code is not shown) that the problem of mismatched loop size and shift width is easily remedied
by preventing the code from compiling when these values do not match. In this case, rather than testing for !
(loop_counter % 16), it would be better to define log2_size as 4, use !(1 <> log2_size when calculating the average.
The critique that the resulting code can only be used for power-of-two sample sizes (and not for a sample size of say,
15) is offset by the fact that the code runs much quicker for these sizes, as both the division and the detection of that the
loop counter is 0 modulo 16 (or whatever). Hence, run-time is quite non-linear in the choice of sample size, and if the
underlying problem is robust in the sample size (quite likely, since we are calculating averages), it makes sense to
program run-time performance rather than for unnecessary generality.
In fact, coding in this way prevents subsequent maintainers from lightheartedly changing the value such that this
particular section of code runs drastically slower.
In general, integer arithmetic has many interesting properties that one can exploit; compilers cannot do this in general.
More importantly, it has limitations (such as overflow) which prevent it from being applied blindly. Mastering this
requires practice. A piece of code using shifts rather than blindly applying division gives the reader at least some
reassurance that the original coder has given some thought to these matters.
A programmer with a limited vocabulary (i.e., is conversant with only a small number of idioms) is a programmer with a
limited vocabulary. How low shall we go in the sake of readability, that is, limiting our use of idioms?
Reply
david collier says:
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 10 of 23
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 11 of 23
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 12 of 23
I would like to post the recommendation: avoid ==, if you ca use >= or <='
Otherwise any spurious memory corruption will cause disaster.
I've seen the clock stuck at 65 seconds, as an example.
The first line of defence is good HW design for EMC, but second: proper plausibility checks in SW.
regards
Tomasz
Reply
david collier says:
August 31, 2011 at 6:38 am
UTC-leap-seconds-aware programmer code
If ( ++seconds >= num_seconds_in_this_minute ) seconds = 0;
Reply
18. david collier says:
August 31, 2011 at 6:42 am
Rule 1 is not a ruile. The rule should be
be aware that understanding the relationship between shifts and divisions ( or multiplications ) by 2 can sometimes give
you more efficient code.
I have a lovely block of code for an MSP430, which has a 12-bit AtoD. I DMA a block of AtoD values onto a buffer,
and then block add them to another buffer.
After doing this 16 times, the results are averaged 16-bit signed numbers I can use elsewhere.
If written without adequate comments its horrific. With the right comments its a thing of beauty.
Reply
19. Lundin says:
August 31, 2011 at 6:51 am
#1 is not really a programming rule, it is an optimization advise. There are actually still dumb-enough compilers around,
that will not do this shift optimization for you. So knowing about it is good, although it should only be used when one
has verified that 1) The compiler is indeed dumb and 2) that the division is an actual performance problem and not just a
non-existent problem conjured by the programmer.
I wouldnt call #1 bad nor good, it is just a commonly used optimizer trick.
#2 is generally a good rule. The rule doesnt say explicitly that the native primitive data types of C (int, char etc) should
be used. I think everyone agrees that either using stdint.h or typedef:ed integer types is a must, using the native primitive
types is a very bad idea for many reasons.
But the rule rather says: pick a size of the variable according to the need, which is another matter entirely. This is
especially important on small embedded systems (8- and 16-bit MCUs). On the other hand, on 32-bit or larger CPUs,
using anything but 32-bit integers is most of the time a bad idea, because as soon as you do you expose yourself to the
whole ballet of implicit promotion rules: one of the most dangerous features of the C language.
#3 is just strange. Why would you do that for? A common cause for bugs are programmers that assume that the logical
opposite of less than is greater than. While in reality, the opposite is greater than or equal. Anyone with a
software and/or electronics degree has likely been sufficiently exposed to boolean algebra and discreet mathematics to
understand this.
#4 is a very good rule for embedded systems! The main reason is that the C standard states that all variables that are
global or static must be initialized before use. Either to the value the programmer initialized them to, or if they didnt, to
the value zero. This initialization must be done before the program is started, it is required by the C standard. For an
embedded system where code resides in flash rather than RAM, this means that a copy-down snippet must be
executed in runtime before main() is called, which copies down values to all statics/globals.
This copy-down is very bad for several reasons. First of all, it means that as soon as your CPU hits reset, there will be
a startup delay while the copy-down is executed. This causes your program to start up much slower. Which in turn could
be disastrous if the program has some important code at top of main() that must be executed shortly after reset:
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 13 of 23
watchdog setup, low-voltage detect, clock initializations, pull resistor or I/O port enables, mode registers etc etc.
But the copy-down is also bad because it may very well take ages between the copy-down and the time when the code
using the variable is executed. During this time the value sits in RAM, and good practice is to never rely on RAM. High
integrity embedded systems typically forbids such practice, and also requires that all values in RAM are updated from
flash on regular basis.
To avoid all mentioned problems, it is common practice to write code that does not rely on static/global initialization, ie
the code does not rely on initialization at all. Most embedded compilers also have a non-standard option to disable
initialization at startup. If you wrote the code so that it does not rely on initialization, it will be portable even if this -very
- common non-standard extension is present in the compiler.
Ok so what if the variable is placed on the stack (auto) rather than static/global? The programmer must make sure to set
it at some point before using it. If they forget it well, it is no biggie. Since all professional programmers use static
analysers (right?), that will instantly find such simple bugs. Real programming rule standards like MISRA-C explicitly
states that all -automatic- variables should be initialized.
To sum this up, #4 is a very good rule for embedded systems. For RAM-based PC fluff however, it doesnt really make
any sense.
#5 is just personal preference, there is no real advantage/disadvantage to it. It isnt really worth debating.
To use enum to get -better- type safety is however a very bad idea! It is (signed) int by default, and thus it is as bad as
using the standard int type. And then comes all the dangers of implicit promotions again.
const and #defines can be declared unsigned, but enums cant.
Reply
David Brown says:
August 31, 2011 at 2:55 pm
Regarding #2, you misunderstood Michaels point. It is correct that you should often use a type that is no bigger
than you need, especially on small cpus (though be aware that on bigger cpus, code that accesses smaller types is
often slower). The point was that you should use uint8_t rather than unsigned char, etc.
Your comments on #4 show a misunderstanding of some basic C concepts. First, consider statically allocated data
(file scope, or function static data). Following the C standard, they must be initialised before main() starts either
to their specified initial value, or to 0. So if you omit the initialisation value, thinking to set it within the main
code, then the C startup library code will write 0 to the data. In other words, you wont save much time at the premain() stage, since the cpu will have to write 0 to the uninitialised data instead of copying in the real initial value.
And it is generally much more efficient (in time and space) to let the pre-main() routines initialise the data rather
than doing it explicitly in the main code.
If you have actions that must occur as soon as possible after reset, and cant wait for data initialisation, then most
embedded toolchains have hooks for adding your own pre-main() code. And anyway, youve probably got the
hardware wrong after all, reset and oscillator/pll startup typically takes longer than the pre-main() data
initialisation on most processors. So if you think you need something happening that fast after reset, the chances
are high that youll be getting startup glitches anyway.
For auto variables (on the stack, or in registers), C provides no automatic initialisation you need to do it every
time. If the programmer forgets it, then it /is/ a biggie the programmer has written incorrect code, and the
program wont do what it is meant to do. Most professionals use some sort of static analysis (or at least a compiler
with good warnings) but very few work by writing sloppy code and expecting the static analysis to spot the
bugs.
For #5, you are correct that C compilers treat an enum as an int (or unsigned int, long int, or unsigned long int if
necessary depending on the value of the enum constant) though some may be configured to use smaller types (in
the manner of C++). However, you may like to remember that a number literal (such as you get from #define two
2) is also considered an int (or unsigned int, long int, unsigned long int if necessary for its value). It makes no
difference.
Reply
Lundin says:
September 20, 2011 at 8:25 am
#2, no I did not misunderstand his point, I think this is pretty clear in my post.
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 14 of 23
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 15 of 23
Regarding sloppy code I think you should re-read your original comment. I fully agree with you
that you should not thoughtlessly initialise local variables as a way to try to avoid bugs you should
initialise variables to the value you want when you want to use them. (And of preference you should
not define the variable until then, unless you are stuck with a pre-C99 compiler.) But what you wrote
was The programmer must make sure to set it at some point before using it. If they forget it well,
it is no biggie. Since all professional programmers use static analysers (right?), that will instantly find
such simple bugs. That sounds to me like you are suggesting its okay to write bad code and let the
static analyser find the mistakes rather than setting the variables value correctly when it is needed.
Reply
Petr says:
September 28, 2011 at 1:02 pm
Rule #2
What are the dangers of integer promotions?
Unsigned types are zero-extended (so 0xFF becomes 0x00FF), signed types are sign-extended (so -1
becomes -1), like one would expect.
Rule #4 Initialization at startup:
A decent compiler (actually C runtime) has a nonstandard way to execute specified functions before
this initialization is done.
If C runtime does not allow that then editing linker scripts does the trick.
If it is not possible and C runtime allows suppressing the initialization then your main can call the pre
-init functions in C-runtime explicitly and then manually run function in C runtime to do the
initialization.
If user writes a function which relies on the initialization and there is a chance the init will not be
done for some reason then the he can add an assertion to the code.
Only few situations benefit from the pre-init code, it is foolish to want ordinary code to be prepared
for such situation given the available solutions and diagnostic.
Rule #4 & high-integrity:
It is easier to do a full reboot then to re-run initialization of libraries.
RAM usually contains larger amount of non-constant data then constant data, so reinitializing only
constants will improve little reliability.
Reply
20. Phil Ouellette says:
August 31, 2011 at 8:39 am
Rule #4 has some merit.
If you are coding for a microcontroller with a watch dog timer then your first line of code is usually to disable the WDT
until you are ready to turn it back on. If your compiler startup code spends too much time initializing variables then the
WDT interrupt can fire before your first line of code (turning off the WDT) is executed. I ran into this problem once and
it was a bear to figure out what was behind the intermittant startup problem.
Another valid reason for #4 is I prefer to have init functions that set all of the module level globals to a desired state. I
sometimes call these init functions again as a way of performing a partial warm boot. It just makes sense to have
initialization of these variables be under my direct control instead of happening behind the scenes.
I also tend to prefer #5 as well. I often have used quite complicated #defines used to calculate register values for things
like oscillator and timer registers based on human readable inputs (Crystal Speed, Clocks per Instruction etc). These
calculations only need to be performed at compile time and it is pointless to do this at run time.
Here is an example from an 8051 project I wrote years ago:
// System Timer definitions
#define OPCODES_PER_SECOND (CRYSTAL_SPEED/CLOCKS_PER_OPCODE)
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 16 of 23
// Timer 0 reload defined to create 33 ms interrupt rate (roughly 30 ticks per second)
#if (CLOCKS_PER_OPCODE == 12) // Compiled for emulator mode
#define TIMER0_RELOAD (0xFFFF TICK_RATE_MILLISECONDS * OPCODES_PER_SECOND/2000L)
#else
#define TIMER0_RELOAD (0xFFFF TICK_RATE_MILLISECONDS * OPCODES_PER_SECOND/1000L)
#endif
#define LOW_BYTE(w) ((w)&0xff)
#define HIGH_BYTE(w) (((w)>>8)&0xff)
Later I use this #defined value in the interrupt routine
//Reload Real time clock tick rate
TH0 = LOW_BYTE(TIMER0_RELOAD);
TL0 = HIGH_BYTE(TIMER0_RELOAD);
Sure I could have just hard coded the values, but if my processor speed changed I would have to manually recalculate
the timer reload values. This way all I have to do is change the CRYSTAL_SPEED definition and everything that is
effected by a change in crystal speed (baud rate generator, timer init value etc) are automatically set to the correct
values.
Reply
21. _d says:
August 31, 2011 at 10:30 am
You may do the same things with enums instead of macro definitions:
enum
{
//
TIMER0_RELOAD = 0xFFFF TICK_RATE_MILLISECONDS * OPCODES_PER_SECOND/1000L,
TL0 = HIGH_BYTE(TIMER0_RELOAD),
// .. etc
};
Only function-like HIGH_BYTE() and LO_BYTE() in your example need to be a macro. However, in C++0x you may
use constexpr functions instead.
Reply
22. Laz says:
August 31, 2011 at 3:54 pm
Regarding #5, some of you are refering to values within one source module, and others are refering to values that are
shared across the project, including compiling options. You cant use a static const across modules. You can use
encapsulation methods, but isnt that getting extreme?
For example, I would use #define PI (3.1415f) and put that in constants.h, which is included in all modules. As stated
here, I should create a static const in constants.c, containing a function called pi(), which would have a prototype shared
among all modules. A similar situation for system clock parameters. Possibly more correct, but thatd be diffcult for me
to adopt.
I only use enums for lists where the value doesnt matter, eg colors, or indices, and I use the resulting type checking to
make sure I dont cross-use them. Also useful for auto-sizing arrays.
#define for bits always, for reasons described above. These should be local to a module/driver anyway.
Reply
David Brown says:
August 31, 2011 at 4:21 pm
You can happily put static const definitions in a header file. Unless you do something odd like take the address
or change its value, the result will be at least as code space, data space and time efficient as using a #define value.
Its just like using static inline functions in a header instead of function-like macros.
Reply
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 17 of 23
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 18 of 23
//
// Allegedly optimised (but fatally buggy) version which will kill the patient
//
if (AmountOfLifeSavingDrug >MinimumAcceptableLevel-1)
{
AdministerDrugAndSavePatientsLife(AmountOfLifeSavingDrug);
}
It looks fairly similar (if a little bizarre) but it can kill the patient.
Imagine the simplest possible case ; AmountOfLifeSavingDrug & MinimumAcceptableLevel are unsigned integers.
(Ill assume 16 bit integers for the sake of the example but the logic works for signed as well any size)
So, if we wish it to accept any level, then we set MinimumAcceptableLevel = 0 (Or, if the values were signed,
MIN_SIGNED_INT)
In the correct (and intended) version, this is:
if (AmountOfLifeSavingDrug >=0) < Always true
{
AdministerDrugAndSavePatientsLife(AmountOfLifeSavingDrug); 65535u) < Always false.
{
AdministerDrugAndSavePatientsLife(AmountOfLifeSavingDrug); < Patient Killed
}
Overall the advice in that blogpost was riddled with basic errors. (eg: 'char' is not '-128 to +127' .. it is processor (and
compiler!) dependant. It could be unsigned. It could even (on DSPs) be 32 bits the C standard permits it)
It was originally ten rules but it is down to eight now that errors have been pointed out.
I only hope that more will be eliminated before anyone starts taking this seriously.
Mac
Reply
24. Colin Walls says:
September 1, 2011 at 5:36 am
I am unable to find the original posting, but I have to agree with just about every word you say here. I am rather
frightened to hear that someone developing medical instruments has such perverse ideas.
Reply
25. Rubberman says:
September 1, 2011 at 12:25 pm
Rule #1 right on! Shifting for integer multiplication or division by 2 can by useful when every machine cycle counts,
but different processors handle wrapping and overflows/underflows differently. Caveat programmer!
#2 ditto always use appropriately typed variables an int can be anything from 8 to 64 bits, depending upon the
processor and compiler. Bad assumptions make for bad code.
#3 a good example of programming by intention. Make your code clear what your intent was. Even if you have to
maintain it yourself, it will help you remember what you meant in a particular situation.
#4 totally agree! My rule is to initialize all variables as close to the declaration (preferably in the declaration) as
possible. An assignment takes processor time. Initialization in a declaration instance, especially if the value is a
constant, can be handled by the compiler, hence has zero run-time overhead.
#5 using #defines instead of const typed global/local variables is old school for most of this. There are exceptions, but
I try to stay away from the #define structure when possible. The downside? The const variables do take up memory,
whereas #defines do not (generally). Each construct has its own uses. Just make sure they are appropriate for your
situation.
Reply
David Brown says:
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 19 of 23
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 20 of 23
You are also correct that poor coding standards can result in worse code as the programmer is forced to follow the
standard. This is the case with MISRA it has many rules that, IMHO, lead to worse code.
Reply
Lundin says:
September 21, 2011 at 5:00 am
Everything takes up memory, no numbers can be allocated in thin air. The optimization you describe will
merely move the constant from the const segment of NVM or RAM into the program code, where it will
reside together with the op codes. The total amount of used memory data+program will be the same.
Regarding the MISRA-C rant, I agree that MISRA is harmful for junior programmers. If you didnt know
that you cant compare a float with equality operators, then you arent competent enough to comprehend the
quite advanced MISRA document. You must have at least one professional senior programmer in the team
to use MISRA-C.
For professional programmers it is an excellent set of rules, save for a few strange ones that hopefully will
be fixed in the upcoming MISRA C3.
Reply
David Brown says:
September 28, 2011 at 8:20 am
No, you are wrong about the memory use of static const (or #defined values). When then compiler
can see these as immediate compile-time constants, they are almost always more efficient than a
constant value at a particular address in memory. When the compiler wants to use such a value, it can
load it directly into a register or even use it as an immediate operand in an assembly operation
(depending on the target cpu and the type of value). The value will take space as part of the program
code that much is correct. But if the value is in memory as an individual const, then the compiler
must generate an operation to load it into a register from memory that means that the address of this
const is included in the program code, while the const itself is in data memory (ram or flash). You
have approximately doubled the memory cost (depending on the target), and almost certainly slowed
the code.
On top of that, there is the missed optimisation opportunities that could make a much bigger
difference.
When doing embedded programming, if you are at all interested in the efficiency (time or space) of
your generated code, then it is important to understand the architecture of the target cpu, and to take
time to study the compilers generated assembly code for different types of source code. It is much
more effective than making up rules based on common sense.
Im sure you are able to help your junior programmers write reasonable code with MISRA. For my
own part, I will continue to flaunt MISRA rules when it leads to clearer, safer code, or more efficient
code without sacrificing clarity or safety.
Reply
Leave a Reply
Name (required)
Mail (will not be published) (required)
Website
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 21 of 23
Submit Comment
Barr Code
Michael Barr
Michael Barr is an expert on the design of software-powered medical devices and other embedded
computer systems. (full bio)
Pages
Contact Michael
Links
Recent Posts
Recent Comments
Lundin on How to Combine Volatile with Struct
Jan Bennett on How to Combine Volatile with Struct
JoeS on How to Combine Volatile with Struct
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 22 of 23
Tags
architecture baltimore bugs copyright delicious education
Categories
Archives
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012
Page 23 of 23
http://embeddedgurus.com/barr-code/2011/08/dont-follow-these-5-dangerous-coding-sta...
26-11-2012