So, I’m deep in embedded software, or as people in the know call it, firmware. A lot of what I’m dealing with is rookie nonsense. Now, I’m probably more of a journeyman coder at this point, but good high quality code doesn’t just spring from good intention and effort. There are a great many rules and tricks of the trade that can only really be learned from someone else. I, being self-taught, have learned much of what I now know by blindly stumbling through the wilderness until through sheer effort, I find the way out of the woods of impenetrable error messages. I’m going to be sharing over the course of many posts, some of the rules and best practices that I discover along my journey. Hopefully, they’ll shorten the duration of your meanderings. Follow the jump for a discussion of the use of the preprocessor directive, #ifndef.
One recent frustration has been the use of #ifndef when trying to do the right thing and compartmentalize my code. By compartmentalize, we mean to put functionally distinct pieces of code into different files, a .c file, containing functions, and a .h file, containing declarations and function prototypes. The tricky part to me came when trying to tie it all together. You have the .c file and you’ve written your whiz-bang function and you have the .h file which very neatly and ever so wonderfully comments all of it, but what’s the best way to bring them together. Let’s say you have a .c file called cleverly enough file.c and a header file called file.h. The natural thing to do is to put a preprocessor directive (a message for the compiler) saying at the top of file.c saying simply enough:
Now, this would be enough to bind the two files together, if it weren’t for some sticky wickets. The stickiest of wickets comes when you need to use the functions contained in file.h in multiple places. In my program, I have a file called spi.c and another called spi.h. These files contain functions and declarations needed to be able to use, oddly enough, the spi peripheral on the avr microcontroller. Naturally, spi.c contains a declaration:
There are multiple functional areas of code which need to use the spi peripheral and therefore the spi functions. For instance, the filter module control the filter by sending spi messages. I have a module name filter.c and since it needs to use spi function, it too contains a declaration:
Herein lies the problem. Both spi.c and filter.c are source files for my project. If all I have are these two declarations in two source files without some special magic, I can get some goofy confounding error messages telling you things like “such and such variable has multiple definitions.” What’s the remedy? A little bit of #ifndef. Where do you use it? In your header file.
After comments at the top of the file describing its contents, you should have what is called a multiple inclusion guard using #ifndef. Back to file.h, all the definitions, function prototypes, and potentially global variable declarations, need to be in between some #ifndef and #endif. What it look like?
(All your stuff)
It doesn’t matter particularly what follow the #ifndef for the file, but the #ifndef and the #define must match. Having this “if not defined” statement protects you from dreadful multiple inclusion, where multiple files include your header file and cause all sorts of grief and truly strange error messages. You should always use this structure. Do it.