Design Article

Adopting C programming conventions

Jean Labrosse

4/27/2011 1:00 AM EDT

Click here for more info about next ESC.
 This Embedded Systems Conference paper is from the class "Adopting C Coding Conventions - Writing Clean and Safe Code" taught by Jean Labrosse on Tuesday, May 3rd, 2011 in San Jose, CA.

This paper (from the Embedded Systems Conference class of the same name) discusses some of the problems found in a lot of code today and suggests how they can be avoided. There are many ways you and your embedded development team can improve code quality and in the process, become significantly more productive. Techniques are presented to organize project directories, naming files, laying out code, naming variables, functions, and more. Examples are presented for C but most of the concepts apply to other languages.

This article also appeared in May 2011 ESD print issue. Click image to go to digital edition.
Today's competitive world forces us to introduce products at an increasingly faster rate due to one simple fact of business life: having a product out first may mean acquiring a major share of the market. One way to help make this possible is to assure that the mechanics of writing code become second nature. All project members should clearly understand where each file resides on the company's file server, what each file should be named, what style to use, and how to name variables and functions.

The topic of coding conventions is controversial because we all have our own ways of doing things. One way is not necessarily better than the other. However, it's important that all team members adopt a single set of rules and that these rules are followed religiously and consistently by all participants. The worse thing that you can do is to leave each programmer to do his or her own thing. Such an undisciplined activity will certainly lead to chaos. When you consider that close to half of the development effort of a software-based system comes after its release, why not make the sometimes unpleasant task of supporting code less painful?

In this paper, I'll share some of the conventions I've been using for years and I hope that you'll find some of them useful for your own organization. I urge you to document your own conventions because it makes life easier for everyone, especially when it comes to supporting someone else's code.

Directory structures
One of the first rules to establish is how files are organized. Do you place all the source files in a single directory or do you create different directories for different pieces? I like to use a structure similar to that shown in Table 1.


Click on image to enlarge.

Each product (such as ProdName) has its own directory under PRODUCTS\. If a product requires more than one microprocessors then each has its own directory under ProdName\. All products that contain a microprocessor has a SOFTWARE\ directory. The SOURCE\ directory contains all the source files that are specific to the product. If you have highly modular code and strive to reuse as much code as possible from product to product, the SOURCE\ directory should generally only contain about 10% to 20% of the software which makes the product unique. The remaining 80% to 90% of the code should be located in the \SOFTWARE directory (discussed later). The DOC\ directory contains documentation files specific to the software aspects of the product (such as specifications, state diagrams, flow diagrams, and software description). The TEST\ directory contains product build files (such as batch files, make files, and IDE project) for creating a test version of the product. A test version will build the product using the source files located in the SOURCE\ directory of the product, any reusable code (building blocks), and any test-specific source code you may want to include to verify the proper operation of the application. The latter files generally reside in the TEST\ directory because they don't belong in the final product. The PROD\ directory contains build files for retrieving any released versions of your product. The other directories under ProdName\ are provided to show you where other disciplines within your organization can store their product-related files. In fact, this scheme makes it easy to backup or archive all the files related to a given product whether they're related to software or not.

The \SOFTWARE\ directory is where you could store all reusable, non-product specific files. I call these building blocks, and each contains its own documentation.

Next: Page 2




Antoni Lacasta i Sulla

4/27/2011 8:07 AM EDT

For large projects, although you have a powerful PC, it can take a very long time to compile and link the whole project. I would not recommend the #include "includes.h". The finer your dependencies are solved, the better.

The best coding guidelines i have ever seen are these: http://www.scribd.com/doc/3969122/Joint-Strike-Fighter-C-Coding-Standards

Sign in to Reply



Antoni Lacasta i Sulla

4/28/2011 2:55 AM EDT

I forgot to mention ASTYLE. The code formatting is not an issue since we use this tool. It Works grate from and it's freeware. We run it automatically from the makefile before every build.

Sign in to Reply



Exoson

4/28/2011 11:14 AM EDT

The Joint-Strike-Fighter-C-Coding-Standards are available direct from the source without paying a fee to Scribd. www.jsf.mil/downloads/down_documentation.htm

Sign in to Reply



David Brown

4/28/2011 2:06 PM EDT

Another style guide worth looking at is google's:

http://code.google.com/p/google-styleguide/

Sign in to Reply



cdhmanning

5/5/2011 12:00 AM EDT

Absolutely correct.

I spend a lot of time working in the Linux kernel where lots of include files are pulled in and that does not seem to really hurt build time.

If the number of include files is denting your productivity then have a serious look at your dev tools.

Sign in to Reply



DutchUncle

5/5/2011 8:39 AM EDT

It's not a question of the *number* of include files; it's a question of whether each module is pulling in information that it really doesn't need, and thereby becoming coupled in ways it doesn't need to be. If someone is going to use one big include list of all include files, why not similarly just code one module with all of the subroutines in it? After all, one source file should be easier to deal with than dozens of little source files! (Heck, I started with decks of punched cards.) That's not considered good practice any more; we segment code and decompose functionality. The same should apply to data and definitions. It keeps the scope of comprehension more manageable in a person's head, which is much more important than in the compiler.

Sign in to Reply



bjd

5/5/2011 3:56 PM EDT

Well said.

Sign in to Reply



bjd

5/5/2011 3:55 PM EDT

If you have large teams in parallel development environments (i.e. Clear case) and your project is large, pulling in unnecessary includes can significantly impact productivity over time.

Sign in to Reply



bjd

5/5/2011 3:50 PM EDT

I agree. A master include is horrible software engineering practice and can be considered 'lazy' IMO. I know first hand this is a violation of modular coding standards at large tech companies.

Sign in to Reply



cdhmanning

4/27/2011 9:43 PM EDT

Coding style will always be contentious and I certainly don't like the one raised here. It is very seldom that you will start from a clean slate and often that means different files in a project will have different styles and it is more important to try to keep the code easy to read than consistent.

I really doubt that consistent coding style makes programmers more productive because they can read eachother's code. Well wrtitten and well laid out code need not be consistent. I can read uCOS code as easily as Linux kernel code or gcc code and all have very different style.

Coding style has always been an issue easiest to pick on by petty project leaders. If your project reviews are all about code style then you're probably ignoring far more important issues.

What's with all the capital letter names XXX.C YYY.H? Surely lower case names are better? Mixing case is problematic when you need to shift projects from a host OS with case sensitive names to one with insensitive names.


Sign in to Reply



CONSTANTIN.BAJENARU

4/27/2011 10:43 PM EDT

I disagree with defining global variables in the header file.

Sign in to Reply



David Brown

4/28/2011 3:53 AM EDT

I agree with the principle that consistent style is important. However, this particular style seems to have been created some 15 years ago, and left untouched. The world has moved on, and it makes sense to have a style convention that takes advantage of improvements since the days of DOS and K&R C. Consistency is important, especially over long-term projects, but a style guide should be updated as necessary.

Here are a few things I particularly disagree with:

Drop the DOS-crippled file and directory conventions. Use capital letters in names if you want - but not all-caps. There is no need for abbreviations in file names, unless it's obvious what they mean - you can call a file "displayTables.c" if you want. And C files use ".c", not ".C".

Version control software should /not/ modify comment blocks - it should leave files untouched. Most modern VCS software follows that rule.

Making your header files work differently depending on when they are used (based on xxx_EXT macros) is such bad style it makes me cringe. I hadn't expected a professional author and developer - who relies on his reputation - to even suggest it. You declare globals as "extern" in a header, and define them in the matching C file - the compiler will tell you if you get something wrong.

It's been many years since stdint.h has been available - fixed size data types are called int8_t, etc. There is no need to have your own private convention, and certainly no need to shout about it (fix the broken capslock key).

And if your compiler won't accept // comments, get a better compiler.


Sign in to Reply



final_test_engineer

5/3/2011 4:17 PM EDT

Just an aside, the place I was at was mentoring some of the student engineers in K&R C. It was so funny I almost left the room laughing. I had practiced engineering for about 8 years and had not even learned K&R C. The sad thing is, what if they were hired right away, coding K&R C right now.

Sign in to Reply



Dipal.Zambare

4/28/2011 6:55 AM EDT

Having single header file is not a very good practice, even with very powerful computer (I use 2.4 GHZ Core 2 Duo) it is very frustrating if you have to compile more then 10/15 files.

Furthermore if there is problem with one of the included file from the common header file you will get pile of errors while compiling a source file which does not even need any of the declarations from the culprit header file.

For the maintenance project I just stick to the existing coding style.

Sign in to Reply



David Brown

4/28/2011 7:16 AM EDT

There are many reasons not to use a single "master" include file. It's okay to have a common include file for basic functionality that should always be available, such as including stdint.h, perhaps microcontroller-specific includes, and system configuration - every file needs these available.

But outside that, you include the header for a module if you use that module - you don't pollute your namespace with useless names. It keeps your code clearer, easier to maintain, and more modular, and makes compilation faster.

Sign in to Reply



Code Monkey

12/19/2011 3:39 PM EST

If you import all libraries into a module, you can't tell where your dependencies lie just by looking. It goes against the spirit of information hiding. You are also assuming that all function names in all libraries are mutually exclusive, which may not be true.

Sign in to Reply



DutchUncle

4/28/2011 11:10 AM EDT

I use uC/OS, and Mr. Labrosse's product is coded beautifully. That said:

Sounds like a quibble, but in the directory tree, Object and Listings are level with Source. Every system I've ever worked on put them *below* the Source. So even with respect to such a basic decision, there are clearly different "standards". (Don't get me wrong, I love standards, and there are so many to choose from . . .)

I concur with others that header files should be broken up by function, particularly if there are distinct subsystems or tasks. Only the display modules need to know all of the display codes, only the comm modules needs to know the comm codes, etc.

To the person who says "not all caps" - sometimes that's part of the convention (constants in caps, variables mixed or small). As long as a practice is *consistent* it can confer meaning.

File names with blanks were created with Windows purely to destroy compatibility with existing DOS and any-other-OS applications. Space doesn't work in variable names, so don't expect it to work in file names, and learn to type an underscore. :-)

Sign in to Reply



David Brown

4/28/2011 2:04 PM EDT

I agree that spaces in filenames and directory names are a daft idea. But so is limiting names to 8.3 letters, all capitals. Use sensible, meaningful names for files and directories, as long or as short as makes sense - just like for variables.

As to all-caps for things like define'd macros or enum constants - yes, it's a convention. And that convention stretches back to K&R's original habits. But that doesn't make it any less a poor convention - writing in all caps is ugly and distracting, and provides no benefits to coding. Some people think it's a good idea because it makes it clear that you are using a macro - but /why/ is that useful? Why should it matter if a "function" is a real function, or a function-like macro? Why should it matter if an identifier is a macro or a variable? If it makes a significant difference to the code you are writing, then you would already know the answer.

And if you really want to be able to see at a glance which identifiers are macros, then join the 21st century and get an editor with syntax highlighting.

Conventions using all-caps are from the dark ages, when C was created with the single aim of letting K&R write operating system code using fewer keypresses than writing it in assembly (their motivation for developing C was their hatred for the DEC keyboards they had).

Sign in to Reply



EliotWDudley

4/28/2011 6:26 PM EDT

It's disappointing that anyone presuming to offer advice on how to code C has not yet adopted stdint.h or Paul Hsieh's pstdint.h.

But on a positive note, it was nice not to see someone clinging to the notion that Hungarian notation is a good idea.

Cheers --ewd

Sign in to Reply



mneedz

4/28/2011 7:02 PM EDT

I totally disagree with the master include file idea. It leads to less maintainable code that is harder to follow. Which include files did I need to compile this particular source file ? I don't know, but it must be one of the 100 includes in the mega-master include file .... While I agree with a lot of what is being said in this article, please do NOT use the master include file. I just looked back at a relatively simple project somebody did with such a master include file. There are 36 #include statements in a row, and I remember this made it 10x harder to find what I needed at the time. This just makes me crazy.

Sign in to Reply



David Brown

4/29/2011 3:18 AM EDT

I've seen worse program organisation. I once had to maintain a program where there was /only/ a master include file - no other headers at all. Every extern declaration of data or functions was inside this file, in no particular order or relation to the module that defined it.

Other joys of this program included filenames in DOS convention (the compiler was DOS-based, so that's fair enough) but with a program name prefix first "PRG_". This left 4 letters unique to each file name. The programmer had the same attitude to variable and function names - none longer than 8 letters. On the very rare occasions when anything was commented, the comments were also abbreviated.

Sign in to Reply



Haldor

4/29/2011 11:51 AM EDT

I think I worked with the same contractor on a project.

This clown used this convention not just for globals but for automatic variables within function as well. I think this was a deliberate attempt to make the code harder to understand and maintain which in turn would help keep him employed. Here is an actual code fragment from his work.

What is really sad is this could have been replaced with a simple memcpy() function call.

void nvDefVfl(void)
{
unsigned int dvfint;// gp
far unsigned char * dvfdfptr;// flagbits
far unsigned char * dvfdsptr;// flagbits

for (dvfint = nvFdsPtr.size, dvfdsptr = nvFdvPtr.origin, dvfdfptr = nvFdsPtr.inistruc
; dvfint !=0
; dvfint--,dvfdfptr++,dvfdsptr++)
*dvfdsptr = *dvfdfptr ;
nvFdvPtr.insafter = 0 ;// insert required
}

Syntactically I had to replace pointer notation with struct notation because this site won't permit certain characters in comments. Hope the formatting of the code fragment doesn't get munched in my posting.

Don't get me started on the hungarian naming convention on steriods. Not only did the variable names include the data type but also where it was stored (EEPROM, FLASH, Local Processor RAM, External RAM etc). This led to delightful variable type definitions names like FUCBYTE (far unsinged char_byte)

He was also a huge proponent of the giant Globals.c that contained every single global variable used in the project. That way everybody had full visibility into everybody elses variables.

Sign in to Reply



Haldor

4/29/2011 11:52 AM EDT

Well as I feared all the white space got removed from my post. Sorry for the mashup, what I posted didn't look like that.

Sign in to Reply



cdhmanning

5/5/2011 12:07 AM EDT

It probably looks better...

I'd demand that anyone writing code like that get fired.

Hungarian really breaks a very valuable practice called Abstract Data Typing (ADTs). If you have to change the type of a variable during development - what then? You have to go edit all the code.

Sign in to Reply



antiquus

4/29/2011 11:41 AM EDT

Global variables? Comments to the right? You gotta be kidding! I will assume that this arrived on the EETimes editor's desk on April 1. What a hoot!

We no longer write with assemblers. In the modern world of 32-bit processors, data structures, stack frames and global optimizers, global variables should be verboten and nearly extinct. If you work in the 8-bit world, then OK, maybe, you can get away with stuff like that.

And comments like:

newDay = TRUE; /* ..change flag to indicate a new day */

are simply redundant. I bet all your school essays were graded by weight?


Finally, in your code example, there are 4 global variables, and the longest path requires 7 accesses (4 read & 3 write, or maybe 3 reads if the optimizer picked up on the ClkHr++ fetch). You didn't block interrupts -- this is incredibly dangerous in a real-time environment, and among the most difficult debug sessions you will every experience.

A better solution would be to put those 4 variables into a data structure, and pass a pointer to that structure into the subroutine. Then, the structure can be guarded at a higher level of code (for example, with the routine that will also increment the calendar information). An ARM or MIPS CPU is _much_ more efficient at indexing and dereferencing a pointer than it is at pulling up statically allocated memory data, so the resulting machine code will probably be at least 2x faster. Third (but probably not last), by not using globals, the entire routine can be re-used for other coding situations.

Sign in to Reply



David Brown

4/30/2011 3:15 PM EDT

Unnecessary globals are a bad idea. But the variables here may be file statics, which is normally perfectly reasonable. You've got to have your data /somewhere/.

As to whether it is better to have separate variables, or put them in a structure, that depends on the program, the target processor, and the compiler - you can't make generalisations here.


Of course, you are correct about idiotic comments. I don't see a need for any comments in that code sample.

Sign in to Reply



EmbeddedMoose

4/29/2011 12:32 PM EDT

Folks, I may be a bit of a dinosaur here, but I do see the value in a Hungarian notation that contains hints to the variable type. When both signed and unsigned integers with different bit sizes are involved in the code (and don't we see a lot of that in embedded development), or there is a lot of pointers and references [i.e., pointers] to pointers flying around, the Hungarian notation helps to do sort of a "dimensional analysis" :-) just by looking at a statement; no need to scroll up, hover the mouse, etc. to double-check the exact variable types. This is just my royal 2 cents, but we all know how dangerous it is to mess something up on this front...

http://www.microparsec.com/comics/53.html

Sign in to Reply



DutchUncle

4/29/2011 1:09 PM EDT

Until someone changes a variable's meaning, but DOESN'T change the name (especially the prefix) because that would involve changing text all over the place . . . No thanks. Turn on the compiler checking (and if possible promote warnings to errors), and use a static analyzer, and make the names meaningful instead. "Words have meaning, and names have power."

Sign in to Reply



UnderboatBoy

5/3/2011 10:08 AM EDT

I really have interest in this article but as I do this on break the best way for me to actually read the whole thing would be if I could extract a PDF file and read it later...

Do PDF and provide real info distribution.

Sign in to Reply



Darius Pl.

5/7/2011 6:22 PM EDT

Why not to use some print to PDF tool? If you prefer PDF, then it should be in your "most handy tools" list :)

For my self I use One Note for such articles, as this allows convenient article archiving and "global search", even in pictures or figures.

Sign in to Reply



Please sign in to post comment

Navigate to related information

Datasheets.com Parts Search

185 million searchable parts
(please enter a part number or hit search to begin)