Embedded C Traps and Pitfalls
Embedded C Traps and Pitfalls
Embedded C Traps and Pitfalls
3rd Edition
21 December 2001
mailto:quest@phaedsys.org
http://quest.phaedsys.org/
www.phaedsys.org
page 2 of 64
Third Edition
Updated as part of the QuEST series
September 2001
Second Edition
presented at the
Embedded Systems Show and Conference, Olympia, London
24th May 2000 For Hitex (UK)
and (in condensed form)
ESS and Conference, Excel, London
16th May 2001 For Keil (UK)
First edition
presented at
JAva C & C++ conference
Oxford Union, Oxford UK Sept 1999
For the Association of C and C++ Users, see www.accu.org
The slides and copies of this paper (and subsequent versions) and the
power point slides will be available at www.hitex.co.uk or
http://quest.phaedsys.org/the authors personal web site.
quest@phaedsys.org
QuEST Series
QuEST 0
QuEST 1
QuEST 2
QuEST 3
Additional Information
QA1
QA2
QA3
QA4
SCIL-Level
Tile Hill Style Guide
QuEST-C
PC-Lint MISRA-C Compliance Matrix
www.phaedsys.org
page 3 of 64
www.phaedsys.org
page 4 of 64
Contents
1.
Introduction ....................................................................................................7
2.
History ............................................................................................................13
3.
3.1.
4.
Organise ................................................................................................................. 19
Good C...........................................................................................................23
4.1.
Style ........................................................................................................................ 24
4.2.
4.3.
4.4.
Flow Control.......................................................................................................... 35
4.5.
If.............................................................................................................................. 36
4.6.
Switch ..................................................................................................................... 38
4.7.
4.8.
4.9.
4.1.1.
4.1.2.
4.1.3.
4.1.4.
5.
(K&R)............................................................................................................................25
(Indent)........................................................................................................................26
(Exdented)..................................................................................................................26
Information blocks and comments ...................................................................29
5.1.
5.2.
5.3.
6.
7.
7.1.
7.2.
Volatile ................................................................................................................... 50
7.3.
Const....................................................................................................................... 50
7.4.
Register................................................................................................................... 51
7.5.
7.6.
Libraries................................................................................................................. 51
7.7.
7.8.
Maths...................................................................................................................... 52
7.1.1.
7.1.2.
Enumerated Types....................................................................................................49
One way of NOT decreasing the code ..................................................................49
8.
9.
Conclusion ....................................................................................................55
10.
Appendix A (style)........................................................................................57
www.phaedsys.org
page 5 of 64
10.1.
K&R.................................................................................................................... 57
10.2.
10.3.
11.
12.
References.....................................................................................................63
www.phaedsys.org
page 6 of 64
"computer" does not mean PC. This is some 10 years before the PC was born.
www.phaedsys.org
page 7 of 64
As a spin off from the wide spread use of C in embedded systems there are
many support tools, simulators, monitors and ICE that now support C
source level debugging. This makes C an even more efficient way of
producing embedded systems. I have been told that the C compiler is the
most understood (and heavily tested) software on the planet. This in its
favour.
Unfortunately, the History of C works against it. It is seen as a hacker's
language and has a reputation as a read only language. Part of this is due
to the obfuscated C competition to produce the most unreadable and
tortuous, but fully legal, C program. I did have C program that as a single
(long) line. The main() line with an empty pair of braces{}. It would (with
out a word of the text visible in the program) compile and print out to
screen the whole of the 12 days of Christmas from the very strange
executable parameters!
The other problem is that, just as everyone thinks they can write a book
many think they can write a C compiler or debugger. Unfortunately, this
means that there are also a lot of poor quality tools (and a lot more poor
quality books) out there. Choose wisely.
As an example, I was once asked to set up (for a UK University) a cross
compiler. It was a PC hosted Modula-2 68K compiler. However, upon
trying to install it I ran into problems. The compiler, it turned out, had
been written in assembler. The company who produced it had virtually no
design documentation, only the users manual. There was no test suit or
proof of testing. As the programmer had left the company (and the
country), they were not able to help at all.
Thus, a cross compiler for safety critical use with a safe language
(Modula-2) was in fact a totally unsafe piece of software. Had I got the
compiler up and running the University would have had no idea how
unsafe the underlying construction was. It would have been used on
several safety critical projects because Modula-2 was a safe language.
There are many more C tools out there than Mod 2 ones. Therefore, there
are likely to be many more poor quality C tools out there. So, be careful.
Make sure of the pedigree of any embedded development tools you buy.
As any mechanical, civil, aeronautical or electrical Engineer will tell you:
page 8 of 64
15
10
5
0
Late
unit cost
tools
This assumes 20% growth rate, 12% annual price erosion and a 5-year product life cycle.
Source: Kcinsey & Co.
As you can see getting the right tools in may be a large hit in the
development budget but it will pay dividends in the long run. In addition,
the tools will be usable on the next project thus giving even greater
savings or at least minimising the potential losses.
For this paper, I am only interested in the production of good C source
code. I am not looking at how you got your design, CASE tools, how the
teams were organised or anything of that nature. This is largely because
the majority of embedded systems (8 and 16 bit) are on the small side and
do not warrant computerised CASE tools. As Les Hatton once said Without
proper use of the tool a CASE tool can create a mess far more effectively
and efficiently! The correct use of a CASE tool can speed up a design and
reduce errors however the majority these days are aimed at OOP, C++ and
Ada. There are few CASE tools aimed at the smaller C projects.
The second paper in the QuEST series: Micro-controller Debuggers (see
quest.phaedsys.org) deals with the debugging of the code once you have
got it (almost) running on the target. The third paper in the QuEST series:
Advanced Embedded System Testing For Fun (also on
quest.phaedsys.org ) goes on to cover testing, automated regression
testing and what spend you bonus on in your increased spare time having
got the project in before time.
In many cases discussed it is the type of tool not the make of tool that is
important. For example static analysers run from Free to 6,000 each.
However, the most cost effective one for my money costs 127. You will
need to use one but the nature of your project will determine which type of
static analyser is the most appropriate. Note for static analysis you MUST
use one. The question is which!
Why "must"? Well, there are some good technical reasons I shall come on
to later. There are some good commercial reasons, which I shall also come
www.phaedsys.org
page 9 of 64
on to later. Then there are the legal reasons, which the government will
come on to later.
At this point I have to state that whilst I am Eur Ing C. Hills BSc, C. Eng,
MIEE, FRGS none of that lot relates to law and I am NOT in anyway legally
qualified. The following is purely my opinion.
The is an amendment proposed to the Manslaughter Act called "Reforming
the Law On Involuntary Manslaughter: the Government's Proposals" I
found it at http://www.homeoffice.gov.uk/consult/invmans.htm
This is the infamous "corporate manslaughter" amendment. It talks of the
Herald of Free Enterprise disaster, the Kings Cross Fire and the Clapham
Rail crash The point is that it will be possible to sue the entity of a
company (not a person) for Corporate Manslaughter. In the case of the
Herald of Free Enterprise it was suggested that whilst no one person was
not guilty of a specific act, which caused the disaster, it due to the way the
company ran the ship was the cause. Thus the company could be sued for
Corporate Manslaughter.
There are, in my opinion, unfortunately a couple of false hopes in the
amendments. First it is not backdated. It will only affect accidents after the
date it comes in. This was (when I asked the Home office in late 2001)
expected to be in the 2004 Session of parliament.
Why is this a false hope? Follow this logic. The new rules come in during
2005. There is an accident with say a car in 2007. The makers are sued
under the corporate manslaughter rules. The Car makes say it was
WXYZ's sub system. WXYZ say it was the SW written by A-Subbie Ltd.
The problem was this was the sw you wrote in 2002. This (in my opinion)
could be looked at under Corporate Manslaughter. "Not retrospective"
means that you cannot claim "Corporate Manslaughter" on accidents
before the amendments come in. Most of the systems involved in accidents
after the amendments are in will have been developed before this date.
Can you show due diligence? Can you show that you engineered the SW
using good methods and approved principals, the correct tools that were
up to the job?
The other false hope is the words "corporate manslaughter" only appears
in a few places. Most of the acts to be amended such as the Railways Act,
the Aviation and Maritime Security Act, the Bail act, The Criminal Law Act
etc only mention the addition of "Reckless killing" and/or "killing by gross
negligence". . The one place where the phrase "Corporate Killing" does
appear is in the Coroners Act. As far as I can see only a coroner can
invoke "Corporate killing"
This in my opinion may give a false hope. Remember the coroner "only"
investigates when there is a dead body to establish cause of death. A
Coroner would get involved for example in a rail crash, an aviation or
www.phaedsys.org
page 10 of 64
A corporation is guilty of corporate killing if(a) a management failure by the corporation is the
cause or one of the causes of a person's death:
and
(b)
that failure constitutes conduct failing far
below what can reasonably be expected of persons
of the corporation in the circumstances.
(2)
For the purposes of subsection (1) above(a) there is a management failure by a corperation
if the way in which its activities are managed or
organised fails to ensure the health and safety of
persons employed in or affected by those
activities; and
(b) such a failure may be reguarded as a cuase of a
person's death notwithstanding that the immediate
cause is the act or omission of an individual.
www.phaedsys.org
page 11 of 64
www.phaedsys.org
page 12 of 64
2. History
The problem with C is its history. I do not propose to re-tell The K&R
Story [K&R] here however there are some parts pertinent to this paper. I
recommend that people read the paper by Dennis Ritchie [Ritchie] this is
available from his web site:
http://cm.bell-labs.com/cm/cs/who/dmr/index.htm.
C was developed initially (between 1969 and 1973) to fit in to a space of 8K.
Also C was designed in order to write an (portable) operating system.
Unlike today, where disks and memory are inexpensive, at the time Multics
was around operating systems had to take up as little space as possible, to
leave room for applications on minimal memory systems. This makes it
idea for embedded systems.
C was developed from B and influenced by soup of several other
languages. Interestingly BCPL, from which B was developed used // for
comments just as C++ does and now finally C99!
One of the problems with C is that now the majority of people learn C in a
Unix or PC environment with plenty of memory, disk space, native
debugging tools and the luxury of a screen, and keyboard.
Because C was designed for operating systems it can directly manipulate
the hardware and memory addresses (not always in the way expected by
the programmer). This can be very dangerous in normal systems let alone
embedded ones!
C permits the user to do many unorthodox things. A prime example is to
declare 2 arrays of 10 items A[10] and B[10]. Then knowing that (in the
particular implementation in use) they are placed together in memory use
the A reference for speed step from A[0] to A[19]. This is the sort of short
cut that has got C a bad name. Yes, I have seen this done.
The syntax of C and its link with UNIX (famous for its terse commands)
means that many programmers try to write C using the shortest and most
compact methods possible. This has led to lines like:
while (l--) *d++ = *s++;
or
typedef boll (* func)(M *m);
This has given C the reputation for being a write only language and the
domain of hackers.
As C was developed when computing was in its infancy and there were no
guidelines for SW engineering. In the early days many techniques were
tried that should by now have been buried. Unfortunately, some of them
live on.
www.phaedsys.org
page 13 of 64
page 14 of 64
K&R 2nd edition gives the syntax changes and "improvements" in C over the
decade sicne K&R1 and it brought K&R into line with the ANSI C 1989
standard. If you want a K&R for practical use this is the edition to have. You
should remember it is not the definitive as from 1999. I expect there will
not, despite public pressure, be a K&R3 as all the authors have moved on to
new things in the last decade. (The authors previously have stated that
there would not be a K&R3 but in early 2001 they left the door open)
Note the standard takes longer to ratify and publish that a book, which is
why K&R (who were part of the US (ANSI) ISO committee anyway) got their
book out first.
ANSI C 1989
Eventually due to the large number of people using C ANSI produced a
USA standard that became the de-facto world wide standard until 1999.
This stabilized the language and gave everyone (except Microsoft) a
standard with which to conform.
ISO-C89 (1990)
(ISO/IEC 9899 Programming Languages-C)
At the end of 1989 ISO (and IEC) with all it's committees from many
countries world wide adopted and ratified the US ANSI standard as an
International Standard. From this point in theory, if not in practice ISO-C
superceded ANSI C as the definitive standard. However, it should be noted
that the only difference between ISO and ANSI C during the 1990's was the
Chapter numbering. One of the standards had an additional chapter before
the actual standard throwing all the chapters out by one. Paragraph
numbering was the same in both.
(ISO-C Amendment 1 1993)
Multi byte Characters
(ISO-C Technical Corrigendum 1995/6)
Work on the new standard starts.
ISO-C99
ISO/IEC 9899:1999
www.phaedsys.org
page 15 of 64
The good news is, at the time of writing (November 2001) It is likely that a
book publisher will turn both the C and C++ standards in to books at
around the 30 mark!
www.phaedsys.org
page 16 of 64
There are thousands of C books out there Few are really good. Most are
for the desktop (MS & MAC) and Unix. For a good source of book
recommendations try the ACCU at www.accu.org. They have independent
reviews of over 2000 C, C++ and SW engineering books. They do not sell
books though so the reviews are completely independent and written by
working Engineers. The is one infamous review that starts "I did not pay
money for this book and I would suggest that no one else should either.."
There is a list of books in the reference appendix. However, remember:
Most C books are written for the desktop programmer not for embedded
systems. I would still get K&R 1st edition as a historical reference but not as
a first C book to learn from. I have an ISO C standard but that is not a book
to learn from either! You do not buy a dictionary to learn how to write
novels.
One thing to be wary of id that if the book is written by an academic it is
likely to have been written for his course It may well refer to
development boards and other equipment made by him at the university
and not generally available. Also it may assume you are doing or have
done other courses and modules in the collage and therefore miss out
useful information because you will get it on the other course. Not all books
written by academics are like this but do take care when buying.
Incidentally if anyone wants the ISO C99 standard the best place to get it
from is a US web site www.techstreet.com/ncitsgate.html where (as of early
2001) an electronic copy (PDF) will cost you $18.. Some 120 less than
BSI are charging! Strangely BSI wondered why all the UK C panel (who
meet at the BSI headquarters) did not buy the standard from them.
Note:- At the time of writing (summer 2001) BSI were looking at publishing
the ISO C and C++ standards at 30 printed but lose leaf. At the moment
this looks like a good deal unless you have access to [someone elses]
printer that can print double sided and will run off 550 pages for free.
The catch is you will need to be a BSI member otherwise it is 100! The
easiest way of becoming an affiliated BSI member is to join the ACCU for
15 per year.
www.phaedsys.org
page 17 of 64
www.phaedsys.org
page 18 of 64
3. SW Engineering with C
As I and many others [Hatton][Misra] [COX] [Pressmann] have said, when
used properly, C can be as safe as any other High Level Language. For
embedded use there are some additional things one must think about. This
paper, in looking at embedded C, will also cover many things that will be
of use in general C programming.
As mentioned in the introduction I am only looking at the production of
safe, robust C source code. How you got your design, pencil and envelope
(50p) or CASE tool (5,000) is not relevant here. Neither is the design
method though there should have been one.
3.1.
Organise
First, organise your files. Both the code files and documentation. Version
Control (or Revision Control System, RCS) has been around for many
years, yet large number of engineers still do not use it. There are basic
RCS/SCCS systems that run on a one-machine one-user basis up to the
systems that can track files across linked networks and the Internet for
projects strung across many counties in several continents. I have worked
on one of these where there were two engineers whose prime task was to
administer the VCS . Appropriately the system was a large communications
controller.
What is VCS? It is basically a database that will hold all the versions of a
file. Thus when bugs are fixed or other changes made to a file both the
original and the new versions can be stored and retrieved.
As can be seen in the diagram
there are five versions of
caneloni.c
Most VCS systems permit the
labelling of file version. In this
case V1.4 is "Final Release"
The system will have the
ability to retrieve all files
associated with a label.
Usually multiple labels can be
assigned to a file. Thus a
standard module can be used
in several projects. So a
standard comms module could be labels "Release_1", Release_2" and
"Special_2"
www.phaedsys.org
page 19 of 64
When the VCS is linked with a make system it gives the ability to make
Release 1.
Most compiler IDE's will now seamlessly integrate into a VCS.
VCS means that you never loose a file and can recover any version of a file
and therefore create any version of the software. This can be very useful
when major changes are added to a file for the wrong reason and need to
be removed.
It also means that when there is a panic because you suddenly need a copy
of V1.34 (current version V5.601) so you can blow an EPROM to send it out
to a customer because you promised four years ago to support the version
he had for the next five years!
The other very good use of VCS is that it permits developers to get on with
the next version without affecting the current builds. IE one can set up the
VCS to let the test team to get the "Release" version of the file released for
testing. Then there is the choice of fixing the bug in the version the
developer is working on or branching the file to give another copy (still
tracked by the VCS and linked to the original file). Most VCS systems
permit the merging of branches later. This is usually a semi-automatic
procedure. Therefore you can bug fix the current released version and
feed the problems in to the new version to be fixed in the most suitable
way. IE some new functionality may have removed the bugged code
anyway.
VCS also stops two people
accidentally working on
copies of the same file. It
usually requires several
intentional acts (bypassing
passwords and warning
dialogue boxes) to get a
second write-able copy out
and several more
intentional acts to check it
back in in-place of the
original version pulled out.
Often it will require the
intervention of the VCS
administrator. SO it will stop accidents but still permit the bypassing of the
system in emergencies. It is all, of course, fully logged and the files can all
be recovered.
Many modern C development systems have hooks in them to interface to
the VCS systems so that once set up they become transparent to the
developers.
Due to automatic time and date stamping in VCS systems, no matter how
slack you, or some one else gets, you should be able to, this is the part ISO
www.phaedsys.org
page 20 of 64
9000 people like, show a complete audit trail from the day the file was
created. So not only will you be IS)900 compliant it will save you many
hours when someone suddenly needs an old version. As a benchmark in
late 2001 VCS costs ranged from FREE to the average price of 400 per seat
with a few top end systems costing a little more.
VCS systems cover all sizes of project. I have seen an extreme case where
a very large embedded project consisted of several linked systems
produced by a couple of hundred engineers across 9 sites in 6 countries on
three continents. The VCS system (Clear Case from Pure Atria) could not
only track all the files but also synchronise all 9 of the databases
automatically. This meant that all the developers and testers were always
working on the correct (but not necessarily the same) versions of software.
This system was able to cope with two changes of target CPU architecture!
All the reusable Sw modules were kept and moved (with their history) to
the new project.
At the other end I have used a simpler system (PVCS from Merant) that I
found to be very useful on a single machine (or small networks) running
one or several projects. The PVCS suite can also integrate make and bug
reporting modules to give a full ISO9000 and CMM compliant system
complete with audited and documented bug fixes, builds manifests etc.
These systems do cost money and take time to set up, but are worth their
weight in gold when a customer wants a mod done to a project you last
worked on 2 years ago. The other nightmare scenario is where the
customer wants a mod and the code has since modified for something
else.
So, we now have a project where we have organised files where we can get
at any version and easily build any version of the system. Incidentally, this
also helps with testing as test scripts can be held in the VCS in the same
way and any test suite rebuilt. The only thing to watch out for is these
systems store deltas of text files. Most let you baseline and start a new set
of deltas but for the storage of non-ASCII files they usually make complete
copies. This takes up a hell of a lot of disk space so be warned!
What we now need is something in the files we have organised. .Before leaving the
RCS completely there is one last point that goes into the next section. The RCS
systems can usually insert into the source files things like current version, change log,
file names, paths to archive, author etc. In the example shown (PVCS) it is the text
between the $ delimiters. This insertion is automatically done by expanding these
keywords. In this example the whole history block after the $log is also added
automatically.
This automatic insertion means that a simple template is all that is needed
for modules. The developers do not need to complete it, well only the odd
line, as the VCS system does it for them.
In the example below the file name, author, revision, and history log are
automatically inserted. So even with the tardiest of developers an ISO9000
www.phaedsys.org
page 21 of 64
audit trail is automatic. As the log uses the login name specific to the user it
will be obvious who did not correctly complete the header block.
/*******************************************************
** $Workfile: U1CO0001.C $
** Name: Application Block
** Copyright :Keil (uk) 1999
** $Author: Chris Hills$
** $Revision: 1.1 $
**
** Analysis reference:123/ab/45678/001 5.6
** Input Parameters: NONE
** Output Parameters: NONE
**
** $Log: C:/ENG/KOS2/A2C001.C_V $
**
** Rev 1.1 06 May 1998 16:48:28 HILLS_CA
**Issued for review
**
** Rev 1.0 01 Apr 1998 13:09:02 HILLS_CA
**initial version
**
*/
A full style guide, The Tile Hill embedded C Style Guide will be available
at quest.phaedsys.org
www.phaedsys.org
page 22 of 64
4. Good C
Now having organised all the files we need Good C in them. What is
good C?
It must not contain errors
It must perform as expected.
It must be repeatable
It must be easily and clearly readable
These seem simple enough and may at initially appear to overlap.
Firstly, the C should not contain any syntactical or semantic errors. This is
not always as obvious as one might think. Syntactical errors the compiler
picks up but semantic ones can be far subtler. They can also be a lot more
difficult and time consuming to find if left to the test and debug phase to
find. The cost of fixing an error rises almost exponentially the longs it is left
before finding it. A bug found at the time it is coded costs very little to fix
but thousands of pounds and days if it gets in to the field.
The syntactical and semantic errors should be found as the source code is
written using static analysis, not the compiler. The compiler is a translator
not a test tool. Having said that the compiler should always be set to the
highest level of warnings.
The most cost effective way of removing these errors and warnings is static
analysis. This looks at the code without compiling or running it. It can find a
large number of errors and possible errors (Warnings) at the time the code
is written. PC-Lint for example will integrate into most compiler IDE's and
can be used to check the code as it is written.
After the syntactic and semantic errors are removed does the code it do
what you expect? It is of no use having a technically correct program that
does the wrong thing! This is usually the case of understanding the
requirements or quite often things like testing for greater than when it
should have been equal or greater than These problems can usually
only be found by visual inspection (code review) and thorough white box
testing. For catching errors during code inspections, the code needs to be
readable. This is something I will return to later.
Repeatability is one thing that is often overlooked when testing software.
Most software (and embedded in particular) often has to perform the same
tasks many times, sometimes for years on end. I have used a program that
ran well for a while (3 months). It then crashed but after a reset, it ran
again (for about 3 months). It was, under some situations, over writing
buffers. Unlike desktop PCs embedded systems have to be reliable as
they do not have a ctrl-alt-del. On one project, I worked on the life of the
system as 15 years. That is it had to run continuously 24/7 for 15 years.
They were designed to run for 20 years just to be on the safe side.
www.phaedsys.org
page 23 of 64
Style
Style is the often the more contentious area to get people to agree on as it
has no mechanical bearing on safely. I have a few examples that I hope will
show you that having a uniform style (for the whole project) is the best
thing to do.
It is easier to count a group of people if they are standing in lines of 5 and
blocks of 25 than if they are just standing in a group. This is why the army
make troops stand in lines. To prove this for yourself tip a box of paper
clicks on to your desk. Without touching them count them. It is not that
simple and easy to make mistakes. Put the paper clips in to rows of five
and blocks of five rows. Now it is possible to count the number of paper
clips as a glance. Count them again. If they are in a heap it will take the
same time as it did the first time. If they are still in the rows it takes a
fraction of a second.
Likewise when the source code is laid out is a standard way is it far easier
to spot anomalies and errors. It will not take any longer to write it to a
particular house style but the time saved every time you have to read it will
soon mount up. Also as with rows of paper clips that stand out because they
only have four clips in them errors in the source code will be easier to spot.
As a final proof that a standard layout of source code will save time and
reduce errors is email sent to me by one of my team after doing a review
on another teams code:- (N.B. time how long it takes to read what this says)
th
eo
www.phaedsys.org
t
page 24 of 64
her
R
E
O
Rtf
tea
d
mRe
Gua
so
Co
T
ru c
DeLay
Sana
or
ThEoThErTeAmReGuArDsOrUcEc0DeLaYoTaSaNaRtFoRm
Sorry, I meant theo tert eamr egua rdso ruce code layo tasa
nart form or, according to some free thinking software engineers, to
restrict my civil liberties and stifle my creative spirit; The other team
regard source code layout as an art form. I am sure that you instantly
spotted the 0 (zero) in place of the O and the misspellings. In fact now I
come to look at it, the first 3 versions have different errors but I am sure
that was obvious to you!
The illustration above should have convinced you that a uniform style to a
set convention is a good idea. If only when it comes to saving time looking
for the bugs. Don't say, "what bugs?" Zero Defect software is reputed to be
a myth as so few have achieved it. Those that have usually use very strict
style guides.
There are many style guides about. Have a look on the internet or create
your own. For those who want a ready made guide I have produced one as
part of the QuEST series called the Tile Hill Embedded C Style Guide it
is also available at http://quest.phaedsys.org/ .
No mater which style you use do so consistently. There should be a
consistent style on a project not just per engineer. A consistent style across
the whole department or company is better.
NOTE:- (and this is important) Style is about readability. It is easier to spot
mistakes if something is easy to read. Style guides are not about safe code
as such or safe subsets of C. Safe subsets of C we will come onto later.
We now have a religious style debate, which has caused more lines of
emails and messages to newsgroups than lines of code in the programs
referred to! This topic, more heated than any discussion on faith is about
"where to put the braces". There is no One True Faith. The truth is:- Any
system will do as long as you stick to it! Some of the more common are
styles are show here.
4.1.1.
(K&R)
If( xyz){
statement
www.phaedsys.org
page 25 of 64
statement
}
This is the original style from 30 years ago. Because it is the "Original" it is
perceived by many to be "The Way". Some of the (very) old tools look for
this pattern of the opening brace on the same line as the if. Most modern
(ie windows ) tools are happy to accept any style now.
4.1.2.
(Indent)
if(xyz)
{
statement
statement
}
This is a common style that came in after K&R when people realised that
more lines on screen did not take up more compiled space. Also as
screens could show more than 25 lines (remember those?) and were able
to show 50- 60 or more code did not need to be so cramped.
4.1.3.
(Exdented)
if(xyz)
{
statement
statement
}
Extent, my preferred favourite has the advantage that the braces are easy
to spot. And visually link into pairs. (and easier to make up in pairs with a
pencil when printed out.
I have come across some other very strange methods but I would suggest
that the common ones are common because they have been found to work
over the years.
A fuller description of each is given in appendix A. Personally, the
extended is my preferred style but some of the older debugging tools
require the first style. A full style guide, The Tile Hill embedded C Style
Guide will be available at quest.phaedsys.org
There is one place where style and safety meet. The one thing I insist upon
for braces is that they are used wherever they can be used. This is
especially important on things like if clauses for example
interlock = OFF;
www.phaedsys.org
page 26 of 64
if(TRUE == stop)
flag = ON;
interlock = ON;
if(ON == interlock)
open_doors();
else
apply_breaks();
sound_alarm();
This will, obviously, always open the doors and sound the alarm but not
apply the breaks! What it should do is only open the doors if stopped else
apply breaks and sound alarm but not open doors.
I insisted that all code produced with teams I am involved with rigidly
adhere to the principal of always using braces were possible. This may
sound a bit draconian but I have good reason.
I instigated this rule after three of the team spent two days trying to trace a
bug caused by a two line if statement where only one line was actually
inside the if. It caused an error some distance from the if statement and
was not immediately linked to the problem. When the if statement was
considered all three engineers glancing at it saw a correct if statement and
mentally put braces round the two statements. The mind saw what it
thought should be there. The error was combined with another similar
non error to produce a real problem much further away.
The previous code example (according to my pedantic formatting) is
actually the following:
interlock = OFF;
if(TRUE == stop)
{
flag = ON;
}
interlock = ON;
if(ON == interlock)
{
open_doors();
}
else
{
apply_breaks();
}
sound_alarm();
Whereas what was meant was:
interlock = OFF;
if(TRUE == stop)
{
www.phaedsys.org
page 27 of 64
flag = ON;
interlock = ON;
}
if(ON == interlock)
{
open_doors();
}
else
{
apply_breaks();
sound_alarm();
}
This is actually based on a real problem on a rapid transit system in the far
east.. written by programmers in the Midlands! It actually made it as far
as the test runs. The problem was only found by accident after a carriage
broke down and the test train ran with one fewer carriages than normal.
The fixing of this fault cost 50,000 in time, phone calls, faxes, and an
Engineer who went out to investigate.
The code was later given to a Static analysis tool vendor to see if they could
spot the problems that caused the bug (The actual cause that showed the
problem was the reuse of a variable somewhere else).
The static analysis tool that (at the time) cost 5,000 and 2,00 to set up and
configure for the project found the original cause and the problems with
the if statements in 7 minutes. Interestingly I believe it found a few other
"anomalies" that they did not know about. The company bought the tool
and spent a frantic few weeks ironing out a few things and getting an
"upgrade" out to the customer. I refer you to the section in the
introduction that discusses corporate manslaughter!
Static analysis tools start at 127 for PC-Lint (including the configuration) so
you have no reason not to use static analysis. PC-Lint would have picked up
the errors in the case above.
www.phaedsys.org
page 28 of 64
4.1.4.
Comments, or the lack of, are one of the most hotly argued things after
where to put the braces! A full description of commenting is style guide
called the Tile Hill Embedded C Style Guide as part of the QuEST series
it is also available at quest.phaedsys.org.
Each file or module should have an information block similar to the one
below.
/*******************************************************
** $Workfile: U1CO0001.C $
** Name: Application Block
** Copyright :Keil (uk) 1999
** $Author: Chris Hills$
** $Revision: 1.1 $
**
** Analysis reference:123/ab/45678/001 5.6
**
** Input Parameters: NONE
** Output Parameters: NONE
**
** $Log: C:/ENG/KOS2/A2C001.C_V $
**
** Rev 1.1 06 May 1998 16:48:28 HILLS_CA
**Issued for review
**
** Rev 1.0 01 Apr 1998 13:09:02 HILLS_CA
**initial version
**
*/
This is an example that was used in a project under ISO9000. Some of the
significant points are the Analysis Reference to tie the source to the design
documentation and the history log. This should give the developer all the
information on where the file started and who it got to its current state. The
history block in this case is automatically put in by the VCS. Where a VCS
is not used it should be manually maintained.
Each function should also have a simple comment block giving the purpose
of the function, the input and output parameters. Like the main file
information block, where appropriate, the reference to the design or
requirements should be included. This may sound like a lot of extra work
but I have found that it makes one focus on why the function is there.
/************************************************************************************ Convert_One */
/* Name: Convert_one
**
** Purpose: Converts Faranhit to Celsius
**
** Input Parameters
** Return Parameter
**
*/
www.phaedsys.org
page 29 of 64
and line
pointer
echo and
store
increment
line pointer
www.phaedsys.org
and count
page 30 of 64
should be 30% of the file. This figure is only a guide. Remember the
golden rule:-
Header files.
Header files contain all sorts of things such as defines, macros, and function
prototypes that are required in several files. The standard libraries have
header files that are usually included. These are included into the source
using the #include directive. This is a straight textual insert. What can do
wrong? Lots of things.
Since writing the second version of this document I discovered, on a
customers site, a major thing that can go wrong. Not having header files
in the first place!
www.phaedsys.org
page 31 of 64
Why do you need header files? Because header files minimise errors. You
will notice that in C there are two types of include line:#include < stdio.h>
and
#include "myheader.h"
The file in the <> brackets indicates to the compiler that this is a system
file (i.e. the libraries that come with the compiler) and that the compiler
should first look in the compiler include directory. Where as the file in the
"" indicates that the project file is the first place the compiler should look.
This is a pretty large hint that the originators of C thought that you would be
creating your own header files.
There are many reasons why would you want to create your own header
files. To take a simple example think of the stdio.h file. This include the
function prototypes for:extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
When you include sdtio.h it means you don't need to put that lot into every
file you want to use the io functions. In addition, the header file often
contains information on usage. However, with the standard header files the
information is usually in the compiler documentation.
Your own header files will work in the same way. Where you have
functions, defines and macros in a source file that will be used out side that
file they belong in a header file. Note:- a header file should never
contain executable code or declare storage space.
I have seen a case where several C source files were #included into the
main file. This is extremely bad practice. Added to which most debuggers,
simulators and ICE cannot cope with this. As soon as you start to run any
high-level language debugging the system will crash.
Functions used only within the source file should be declared as static and
not put in the header file. See section on static linkage.
www.phaedsys.org
page 32 of 64
F1()
{
}
Main()
{
F2()
{
}
F1();
F3()
{
F6();
F2();
F4()
{
}
F5()
{
}
F6()
{
}
Should for example the prototype for F6 change you will have to change it
in Module.c and module.h, this will automatically change the "extern f4();"
in every file where the function is used. All you then have to do is change
the actual calls. A static analysis tool like PC lint will show up the uses.
The alternative, without using header files, is to change all the "extern F4"
lines in all the modules where it is used. The problem here is that quite
often, programmers being lazy, all they do is cut and paste the new version
in. You then get lots of unused prototypes cluttering up the place and
causing a nightmare in the future.
The other useful point about header files is that, as with the standard
library, it gives the key to the interface to the module. The *.c file can be
compiled to object code and all the information the programmers need to
use the module should be in the header file. You must remember to
update the information in the header file should the usage of the functions
change.
Header files should not be nested.
www.phaedsys.org
page 33 of 64
Nesting hides files. On one project I worked on in one file there were 8
include files. However, these files had nested files. When I unravelled, the
nested headers there were over 120 included files. This included a set of 8
files ten times! The interesting point was that when the duplicates were
removed the source file would not compile! It appeared that due to some
problem no one could be bothered to find three of the files had to be
included twice. Once at the start of the #includes and once at the end. This
was masking a serious problem.
For example something may be defined in one header, turned off or
redefined in another and as the two get repeatedly included parts of the
overall included files will have the define in and others not or the valued of
the define changed. This can have some very strange, and almost
impossible to find effects.
In order to make sure only one of each header is ever included guards
should be used. These guards take the form:
#ifndef name
#define name
header file contents
#endif
Generally for name I use the name of the header file. For example
_STDIO_H or _MODULE1_H
An additional tip is to always include header files in the same order. I
usually start with system headers at the top, followed by any general
project files then the rest of the files. Some people insist that they include
files in a set order, or alphabetically etc. Any system is better than none. It
pays off in the long run usually when bug hunting at 16:30 on a Friday
with a Monday morning delivery.
Incidentally when putting macros into headers use parenthesis
enthusiastically. To ensure there are no silly side effects. I.e. the macro is
self-contained. Macros should only really be used for constants and
function like macros. Using a macro for something like :
#define STARTIF IF(
should be avoided at all costs. In all cases they should start and end with ( )
unless the are a single item.
www.phaedsys.org
page 34 of 64
4.3.
Magic Numbers
Defines & magic numbers. I hope I do not need to tell people not to use
magic numbers, #defines or const should be used. The advantage of using
const is that it will be visible in debuggers. The disadvantage is that it will
actually take up space in ROM or RAM as a (const) variable.
So whilst const is preferable technically, pragmatically #defines may be
better in 8 bit systems like the 8051 where RAM is at a premium. As an
example of using defines read the following code.
case 0x01:
transmit_handler(0x10);
reset_machine();
if (timed_out)
{
transmit_handler(0x60);
}
else
transmit_handler(0x20);
break;
// Reset Machine
With this code one has to guess at what is going on. The reader has no idea
what case 0x01 is. Or how it is related to any other of the hex values in the
code. Now see the same code with the magic numbers changed to defines.
case
RESET_MACHINE:
transmit_handler(BUSY);
reset_machine();
if (TRUE == timed_out)
{
transmit_handler(TIMED_OUT);
}
else
{
transmit_handler(READY_RESET);
}
break;
The second version is readable and less prone to errors. If the define is
wrong it is more likely to through up symptoms across the whole program.
In many cases the use of defines (along with meaningful variable and
function names) can go half way to documenting the source.
4.4.
Flow Control
www.phaedsys.org
page 35 of 64
4.5.
Something I have found effective in tests for equality if to have the fixed or
constant value on the left and the variable on the right. This is the opposite
of the common way of writing it eg
If(variable == constant)
This causes an error if, inadvertently, the test for quality is inadvertently
changed to an assignment. For many years, I have written
if(constant == variable)
This is counter intuitive and does take a while to get used to. However, it
does stop many silly errors. Though with a good compiler and rigorous
use of lint any errors of this type should be picked up whichever way it is
done.
Logic is one of the problems when using if with else. Where if else if is
used there must always be a final else clause. This should be done even
www.phaedsys.org
page 36 of 64
when the final clause will be empty! A comment should be placed in the
final else to say why it is empty.
If( Clause)
{
statement;
}
elseif(clause)
{
statement;
}
else
{
statement;
/* or comment*/
}
This makes it clear why the clause is empty and makes the developer think
about the structure of the whole construct.
Also where there are two mutually exclusive ifs such as
If(True)
{
}
if(False)
{
}
should be written as
if(true)
{
}
else
{
}
If it is not true it must be false? This may not be true where True >=1 and
False == 0 . What happens if somehow the value is -1 Dont assume that the
value will only be the ones you expect.
www.phaedsys.org
page 37 of 64
4.6.
Switch
Notice the break on the default. Always a good idea just in case the default
becomes a case.
4.7.
In a word dont. Break should only be used at the end of every case and
default clause in a switch statement and nowhere else.
Goto.. This needs no comment, as it should never be used. Neither
should continue.
Whilst on the subject of jump out of a flow it is a moot point whether there
should be a single point of exit from a function. Many say there should only
be one return. Others say that for readability and sensible flow in a
function more than one is better. I have seen cases where the function
contained some horribly complex if else if, constructs in order to get one
return line whereas it was far cleaner, elegant, shorter and faster with
several return statements.
www.phaedsys.org
page 38 of 64
4.8.
Static linkage
Variables should be initialised before they are used. In keeping with the
general rule of explicit rather than implicit variables should be explicitly
initialised as soon as possible. The obvious and most sensible time is when
they are declared.
static int count = 0;
static signed char letter =a;
This ensures that all variables are initialised. Variables should always be
declared initialised at the start of a file or function.
www.phaedsys.org
page 39 of 64
www.phaedsys.org
page 40 of 64
interlock = OFF;
if(TRUE == stop)
flag = ON;
interlock = ON;
if(ON == interlock)
open_doors();
else
apply_breaks();
sound_alarm();
If lint was run over this code it would complain that lines 5 and 11 had
incorrect indentation. In fact on PC-Lint it complained:
Warning 539: Did not expect positive indentation from line 5
Warning 539: Did not expect positive indentation from line 11
If you refer to the PC-Lint manual it gives an if statement without braces as
the example!
www.phaedsys.org
page 41 of 64
Another subtler problem on the same lines, from the lint manual, is:
if(.)
else
if(.)
statement
statement
The else is in fact part of the second if, not the first. This is where a good
style guide is useful so the code is uniform and insisting that braces be
used on ALL if, do and while clauses.
Since lint was developed, there have been great strides in static analysis.
This is where the analysis of the source code without compiling it or
running it. HP has estimated that static analysis and code inspections are 5
times more efficient than white or black box testing. Alcatel have said that
static analysis can reduce a project time by up to 30%, primarily from the
debugging and fixing stages. The cost of fixing a source code error (other
than syntax) rises exponentially the longer it it left and the further down the
development it goes. Thus the most cost effective way of fixing sw errors is
at source when the engineer is writing the code.
I use the write and lint cycle instead of the more common write and
compile cycle to check the code.
Lint is not the only tool. It was the first one for C and in keeping with its
Unix/C roots is a simple command line program. Other heavyweight static
analysers (that are also vastly more expensive and time consuming to set
up)
Apart from using lint always run the compiler on its highest level of error
checking.
MISRA-C is a very good set of rules for using C in safety critical embedded
situations. PC-Lint has a configuration file to test for as many of the MISRAC rules as it possible statically. MISRA-C also shows how to construct a
conformance chart. I would recommend this to any developer who needs
to show due diligence or prove that the system has been tested.
5.1.
Formal Eastern European Writing
There can not be a discussion on safe C, proper C without someone
bringing in the two methods for producing perfect code. These methods in
theory are very good. They are usually put forward by theoreticians. Their
practical use in real sw engineering is another matter.
Both the methods should, in theory, eradicate many errors and mistakes
but, in my view, create more than they solve.
www.phaedsys.org
page 42 of 64
5.2.
Hungarian Notation
This is a method were the name of a variable conveys information as to the
usage and the type of the variable. This method sounds wonderful until a
type is changed part way through development. It also does not help
readability. There are also several standard notations in use and countless
local ones. This breeds confusion.
An example of Hungarian notation from Steve Mconnells Code Complete. A
book well worth reading
For(ipavariable = paFirstvariable; ipavariable <= paLastvariable; ipavariable)
{
}
further examples:
ch
a variable containing a character
ach array of ch
ich
index to an array of ch
ichMin
indest to first character in array
ppach pointer to pointer to array of ch
mhscrmenu
m
module level
h handle
scr to a screen region
for a menu
This is logical BUT I have found that these methods usually cause far more
trouble they are worth. There appears to be no general standard. After
you learn, one some one changes it. I have seen a project where it was 2
weeks before the team discovered that some of the team, whilst using the
same letters were using them to signify different things to the rest of the
team!
5.3.
Formal Methods
Formal methods are a almost a mathematical way of describing a program.
They are NOT a programming language though there are some
interpreters for at least two of the languages (Z and VDM)
The problem is that there are two interfaces. One takes the specification
and turns it into Z or VDM and at the other side the conversion from the
formal method to the programming language of your choice.
The interesting thing is that due to the absolute certainty of people in these
methods it can cause problems. Folk history has it that a well known CPU
vendor used formal methods in its chip design, for the microcode. When
several thousands of these chips were produced (at a cost that could
www.phaedsys.org
page 43 of 64
www.phaedsys.org
page 44 of 64
6. Embedded Engineering
Embedded Engineering whilst having many similarities with ordinary
SW Engineering is different. Different that is to ordinary SW
Engineering and every other embedded project. Whilst embedded
systems share many similar attributes no two are the same.
In general, embedded systems tend to be single task. Albeit that the
larger systems may have an operating system and many processes
running. They usually have to meet deadlines that are far tighter than
general-purpose systems. This is because they often have to react to
inputs that are measured in microseconds not seconds or minutes.
Another difference that is crucial is that embedded systems are usually
built to a minimum cost with little or no room or even facility for any
expansion. That is unlike the desktop PC the resources such as memory
are cut to the minimum required for the job and sometimes . This is
because, usually, the embedded system is a small part of a larger system.
The control system is ancillary to the main function of the system for
example a microwave cooker. If additional recourses are added the cost of
the product goes up or the profit goes down.
The problem, for the programmer, is that often memory saving techniques
have to be used. However the most dangerous problem is memory leaks. I
once worked on a comms system where a series of line connections caused
a byte of memory to be lost. One of the engineers did some calculations
and worked out that the unit would fail in 2 to 4 years of use. The other
problem was that depending on usage that the unit had the failure
symptoms could be wildly different. The unit had a lifetime of 10 years and
many units would be in remote sites.
There is one other very important aspect of embedded systems they tend
to be used with mechanical equipment that moves. Whilst not all
embedded systems are safety critical many are. The others will cause
problems if they fail that are usually more of a problem than having to
reboot a PC
www.phaedsys.org
page 45 of 64
www.phaedsys.org
page 46 of 64
7.1.
SIZE MATTERS !
www.phaedsys.org
page 47 of 64
Use appropriate sizes of data types. This may sound obvious but it is
surprising how many people make incorrect assumptions. I have been told
that short is more portable than an int and that a short is always 8 bits.
Neither statement is correct.
Objects may be nonintuitive sizes, Pointers are not always the same size as
ints, the same size as each other, or freely interconvertible. The following
table shows bit sizes for basic types in C for various machines and
compilers.
type
pdp11
series
vax
68000
family
Cray-2
Unisys
1100
Harris
H800
80386
char
short
16
8/16
8/16
64(32)
18
24
8/16
int
16
16/32
16/32
64(32)
36
24
16/32
long
32
32
32
64
36
48
32
char *
16
32
32
64
72
24
16/32/48
int *
16
32
32
64(24)
72
24
16/32/48
int (*)
16
32
32
64
576
24
16/32/48
Type
Minimum
# Bits
char
8
short
16
int
16
long
32
float
24
double 38
any *
14
char * 15
void * 15
No Smaller
Than
char
short
int
float
any *
any *
www.phaedsys.org
page 48 of 64
I use the typedefs shown below. Note I have not used int in any of the
names. This is because much of the time it is a container for data rather
than an integer.
typedef unsigned char
typedef signed char
uint8_t;
int8_t;
uint16_t;
int16_t;
uint32_t;
int32_t;
These typedefs are placed in a short universal header that is included in all
files. Short should only be used where it is larger than a char and smaller
than an int. For example:
Char
Short
Int
Long
= 8 bits
= 16 bits
= 32 bits
= 64 bit
The use of lint with strong type checking enabled will cause warnings to be
issued where one type is assigned to another even if the underlying type
for both is an int.
7.1.1.
Enumerated Types
www.phaedsys.org
page 49 of 64
7.3.
Const
Const should be used where something is not going to change. One place
it is very useful is in function parameter declarations. For example
static U8 func( const U8 name, const U8 number);
This will have both the compiler and lint screaming if the function tries to
change the values.
Interestingly enough it is possible to have a const volatile. The application
can not change the const but it will change in between accesses without the
application touching it.
www.phaedsys.org
page 50 of 64
7.4.
Register
Register is often used to speed things up. However is usually has the
opposite effect! This is because the compiler is very good at shunting
things in and out of registers and memory. Also it is only advisory, many
compilers ignore it. In those that do not forcing a variable into a register
could give the compiler problems sorting the other variables. In the end,
you get a slower system.
7.5.
Dynamic memory
Tuning Libraries
www.phaedsys.org
page 51 of 64
space, improve speed and you know how they work. It pays to re-do most
of the functions that can accept variable numbers of parameters.
7.8.
Maths
Maths is one major problem in embedded systems because the maths
libraries tend to be large. There are ways around this. For some things
like sin and cos a look up table can be used. Whilst this takes up space this
is data not code (highly relevant to an 8051 architecture) it can run much
faster than a function that actually calculates the sin or cos.
Another way of speeding things up is to revert to fractions. To get 75% of
100 look at as or 100. Take 100 do an integer divide by 4 and an integer
multiply by 3. No floats are needed.
Incidentally never do comparisons with floating point numbers. Rounding
errors can play havoc.
The other taboo is playing with the bit fields inside a float. There is no
global standard defined. There may be a de-facto standard but it is not
worth the risk.
www.phaedsys.org
page 52 of 64
8. Embedded C++
As C++ (and OO) became the in thing in the normal programming
world so C++ is becoming sought after in the embedded world. This is to
some extent fashion. I have seen many people looking for C++ compilers
for the 8051 because C++ is better than C.
People also want to use C++ compilers when writing C because C++ is a
superset of C This was true many years ago however the two are now
distinctly separate languages. Having discussed the matter with members
of the UK ISO C and C++ standardisation committees I understand that
there are come parts of C and C++ that have the same syntax that mean
different things. So, do NOT use a C++ compiler for C.
I believe that C++ is too large for 8 bit systems and indeed many
architectures (8051 for example) do not suit the language. C++ is also in
its infancy for embedded 16 bit systems. I have found that C++ works in
32 and 64 bit systems. Indeed Embedded C++ (EC++) is being primarily
aimed at 32 bit systems and will obviously work on 64 bit systems. I can
not see any real incentive to develop EC++ backwards in to the smaller
CPU.
Of course, compiler vendors would like everyone to go from C to EC++ as
this will mean more compiler sales. This leads on to the other problem
with C++ that the development tools (and particularly the debug tools) will
be very complex and expensive. If you are on a cost conscious project,
and who isnt, the buzzwords OO and reuse.
Just as C was a step away from the hardware compared to assembler C++
is a step further away. EC++ will need far more RAM than its C
counterpart. However C++ also creates classes and objects on the fly. The
opportunity for memory leaks is very high. EC++ is being developed to
counter some of these problems. Things like templates will not be in
EC++.
I think that in the next two years C++ will be viable for 16 bit systems
upwards. I am still not convinced that this is a good idea in terms of speed,
memory resources and deterministic response. However, I would not
advocate its use in 8 bit systems even if it does become available.
www.phaedsys.org
page 53 of 64
www.phaedsys.org
page 54 of 64
9. Conclusion
Embedded Engineering is just that. An engineering discipline. Like
architecture, embedded engineers should use the discipline of proper
construction methods and work within the rules. With practice, this will
produce robust and safe systems automatically (and quickly).
Once one has got over the learning curve of doing things rigorously, ones
mind is free design with flair. Most of the worlds great buildings were
designed using standard bricks or frames made from standard girders to
stringent (and long) building regulations. Builders and architects who have
been shown not to use the correct methods end up in court or no longer
able to practice.
As the IEE, BCS, Engineering Council and the government push to raise the
status of Engineers in the UK embedded engineers will be expected to
come into line with other professions. IE using defined construction
methods.
The game is changing and you WILL be judged by its rules weather you
want to play or not.
It does not take much to use Lint, MISRA-C, to use version control (this is
required for ISO9000 anyway). The costs for these tools usually repay
themselves in the shortening of debugging on the first project. The
potential savings are enormous if it saves you having to go to court.
I am assuming that you are of course, using a good compiler and
debugger. It is of little use writing good solid embedded C if you then use
a doggy compiler or an intrusive ICE. Tools are a whole new ball game
explained in Microcontroller Debuggers Their Place In The
Microcontroller Application Development Process [Hills] Getting the
language in to a robust state is one thing. Having the (appropriate) tools of
the same quality to support it is another.
Good SW Engineering practice saves you having to thing about much of
the trivial time wasting parts of a project and lets you get on with innovative
and safe designs.
I recommend that you read MISRA-C and Konigs Traps and Pitfalls.
I shall finish with the last line of the introduction:
www.phaedsys.org
page 55 of 64
www.phaedsys.org
page 56 of 64
10.
Appendix A (style)
I do not intend to go through a complete style guide. These are a few notes
to help you decide which of these common styles suit you (if any).
Indentation is up to the user as long as it is consistent. That is consistent
across a project not per developer.
There are many styles freely available on the Internet. It is not the end of
the world if you do not get to use your per scheme.
10.1.
K&R
This is the original style. However, this has largely been superseded.
Especially with the change to ISO C where the parameter types are
specified in the function line not below it.
Some people still use this because it is the correct way of doing things
and site K&R (First edition). C is over 20 years old and even the
originators, enlightened as they were, have moved on and learnt more.
There is nothing wrong with this style (obviously moving the type
declarations) but it is not the definitive style.
The only word of warning is that some debug tools (usually the older ones)
assume this style. They require the opening { to be on the same line as the
if, do etc.
void change_KandR( from, to)
char *to
char *from
{
do{
if(a == *from ){
*to =A;
}else{
*to = *from;
}
++to;
++from;
} while( \0 != to[-1] );
}
www.phaedsys.org
page 57 of 64
10.2.
Indented Style
This is a later style than K&R. It requires more space on screen and on
paper BUT it takes up no more room when compiled. This more open style
crept in when screens gained colour, windows and more than 80 columns
by 40 lines.
void change_case( char * from, char *to)
{
do
{
if(a == *from )
{
*to =A;
}
else
{
*to = *from;
10.3.
}
++to;
++from;
}
while( \0 != to[-1] );
Exdented Style
My style
www.phaedsys.org
page 58 of 64
Over the years, I have found I prefer to use Exdented (when nothing else
was specified). This is because I find that when doing code reviews it is
easier to spot the pairs of braces round a block. I usually join the pairs of
braces using coloured pencils.
A full Embedded C Style guide is being developed as the Tile Hill
Embedded C Style Guide and will be available on the authors web site
quest.phaedsys.org
www.phaedsys.org
page 59 of 64
www.phaedsys.org
page 60 of 64
11.
www.phaedsys.org
page 61 of 64
C51 Output
When compiled with the C51 compiler, the BADCODE program generates
the following errors and warnings:
MS-DOS C51 COMPILER V5.02
Copyright (c) 1995 KEIL SOFTWARE, INC. All rights reserved.
*** ERROR 315 IN LINE 10 OF BADCODE.C: unknown #directive 'incldue'
*** ERROR 159 IN LINE 12 OF BADCODE.C: 'typelist': type follows void
*** WARNING 206 IN LINE 19 OF BADCODE.C: 'fer': missing function-prototype
*** ERROR 267 IN LINE 19 OF BADCODE.C: 'fer': requires ANSI-style prototype
*** ERROR 141 IN LINE 19 OF BADCODE.C: syntax error near ';'
*** ERROR 141 IN LINE 19 OF BADCODE.C: syntax error near 'OOO'
*** ERROR 202 IN LINE 19 OF BADCODE.C: 'OOO': undefined identifier
*** ERROR 141 IN LINE 19 OF BADCODE.C: syntax error near ')'
*** WARNING 206 IN LINE 21 OF BADCODE.C: 'printf': missing function-prototype
*** ERROR 103 IN LINE 24 OF BADCODE.C: '<string>': unclosed string
*** ERROR 305 IN LINE 24 OF BADCODE.C: unterminated string/char const
*** ERROR 141 IN LINE 25 OF BADCODE.C: syntax error near 'printf'
C51 COMPILATION COMPLETE. 2 WARNING(S), 10 ERROR(S)
PC-Lint Output
When the same code is parsed by PC-Lint, the BADCODE program
generates the following errors and warnings:
--- Module: badcode.c
badcode.c 10 Error 16: Unrecognized name
badcode.c 10 Error 10: Expecting end of line
badcode.c 12 Error 66: Bad type
badcode.c 12 Error 66: Bad type
badcode.c 19 Info 718: fer undeclared, assumed to return int
badcode.c 19 Info 746: call to fer not made in the presence of a prototype
badcode.c 19 Error 10: Expecting ','
badcode.c 19 Error 26: Expected an expression, found ';'
badcode.c 19 Warning 522: Expected void type, assignment, increment or decrement
badcode.c 19 Error 10: Expecting ';'
badcode.c 19 Error 10: Expecting ';'
badcode.c 21 Info 718: printf undeclared, assumed to return int
badcode.c 21 Info 746: call to printf not made in the presence of a prototype
badcode.c 23 Info 737: Loss of sign in promotion from long to unsigned long
badcode.c 23 Info 713: Loss of precision (assignment) (unsigned long to long)
badcode.c 24 Error 2: Unclosed Quote
badcode.c 25 Error 10: Expecting ','
badcode.c 26 Error 10: Expecting ','
badcode.c 26 Error 26: Expected an expression, found '}'
badcode.c 26 Warning 559: Size of argument no. 2 inconsistent with format
badcode.c 26 Warning 516: printf has arg. type conflict (arg. no. 2 -- pointer vs. unsigned int)
with line 21
badcode.c 27 Warning 550: fellow (line 15) not accessed
--- Global Wrap-up
Warning 526: printf (line 21, file badcode.c) not defined
Warning 628: no argument information provided for function printf (line 21, file badcode.c)
Warning 526: fer (line 19, file badcode.c) not defined
Warning 628: no argument information provided for function fer (line 19, file badcode.c)
www.phaedsys.org
page 62 of 64
12.
References
Beach, M. Hitex C51 Primer 3rd Ed, Hitex UK, 1995, http://www.hitex.co.uk
COX B, Software ICs and Objective C, Interactive Programming Environments, McGraw
Hill, 1984
www.scil-level.org
www.phaedsys.org
page 63 of 64
Chris@phaedsys.org
http://www.phaedsys.org/
http://www.hitex.co.uk/
mailto:chills@hitex.co.uk
www.phaedsys.org
page 64 of 64