Saturday, December 22, 2012

What we are doing for ages need not be the best thing that we are doing

Original Source:

What sorts of things should you (or should you not) put in a C language .h header file? When should you create a header file? And why?
When I talk to embedded programmers about writing device drivers or using real-time operating systems or my Embedded C Coding Standard book, I often come to see that many lack basic skills and information about the C programming language. This is probably because we are mostly a gang of electrical engineers who turned to programming and are self-taught in C (and C++ and the myriad other programming languages we make use of).

Dos and don'ts

In the interest of promoting the general welfare, I'd like to use this month's column to discuss one of those basic skills that is too often lacking: building proper header files. Here's my list of Do's and Don'ts for embedded C programmers to follow when creating .h header files.

DO create one .h header file for each "module" of the system. A module may comprise one or more compilation units (e.g., .c or .asm source code files). But it should implement just one aspect of the system. Examples of well-chosen modules are: a device driver for an A/D converter; a communication protocol, such as FTP; and an alarm manager that is solely responsible for logging error conditions and alerting the user of the active errors.

DO include in the header file all of the function prototypes for the public interface of the module it describes. For example a header file adc.h might contain function prototypes for adc_init(), adc_select_input(), and adc_read().

DON'T include in the header file any other function or macro that may lie inside the module source code. It is desirable to hide these internal "helper" functions inside the implementation .c file. If it's not called from any other module, hide it! (If your module spans several compilation units that need to share a helper function, then create a separate header file just for this purpose.) Module A should only call Module B through the public interface defined in moduleb.h.

DON'T include any executable lines of code in a header file, including variable declarations. But note it is necessary to make an exception for the bodies of some inline functions, about which more below.

DON'T expose any variable in a header file, as is too often done by way of the extern keyword. Proper encapsulation of a module requires data hiding: any and all internal state data should only be in private variables inside the .c source code files. Whenever possible these variables should also be declared with keyword static, which will enlist the linker's help in hiding them.

DON'T expose the internal format of any module-specific data structure passed to or returned from one or more of the module's interface functions. That is to say there should be no "struct { … } foo;" code in any header file. If you do have a type you need to pass in and out of your module, so client modules can create instances of it, you can simply "typedef struct foo moduleb_type" in the header file. Client modules should never know, and this way cannot know, the internal format of the struct.

Inline functions

On a related note, I recently received this question from an engineer in Brazil:

"I am trying to conform to the rules in your Embedded C Coding Standard book and I just ran into what may be a problem with Rule 6.3.a. Instead of using function-like macros, I'm using inline functions, as you recommend. However, my compiler (avr-gcc) gives an error when I declare a function to be inline at both header and source file. If I put both the inline declaration and function body inside the header file it works fine. This fixes my compiler problem, but isn't it a bad practice to place code inside the header file?"

This is a good question, as it seems at first to be about a conflict between my bug-killing rules and something else I refer to as "Generally Accepted Programming Principles" (i.e., GAPP).

The inline keyword is a part of the C++ programming language that was added late to C (in C99). In C++, most programs are built out of classes–with GAPP dictating one header file per class definition. Any C++ function may be declared inline. However, if the inline function is a public member function (a.k.a., "public method") of the class it is necessary to place the code for the inline function in the header file. (This is so that all of the other modules that use the class can see the code they need to have placed inline by the compiler.)

Of course, placing the body of any function inside a header file conflicts with GAPP for the C programming language. Here's a set of rules to help you decide where to put the code for inline functions in C.

IF the inline function is a "helper" function that's only used inside one module, THEN put it in that .c file only and don't mention it in the header file. This is consistent with Rule 4.2.c of my Embedded C Coding Standard, which says that "The header file shall identify only the [functions] … about which it is strictly necessary for other modules to know."

IF, however, the inline function operates on the abstract data type defined in the header file and must be visible to two or more modules, THEN put the body of the inline function inside the header file. There is no rule in the Embedded C Coding Standard that strictly prohibits this, so there is no conflict after all.

Though not really specific to embedded software development, I hope this advice on good C programming practices is useful to you. If it is please let me know and I will provide more C advice like this in future columns.

Michael Barr is the author of three books and over sixty articles about embedded systems design, as well as a former editor-in-chief of this magazine. Michael is also a popular speaker at the Embedded Systems Conference, a former adjunct professor at the University of Maryland, and the president of Netrino. He has assisted in the design and implementation of products ranging from safety-critical medical devices to satellite TV receivers. You can reach him via e-mail at or read more of what he has to say at his blog (

No comments:

Post a Comment