I introduce C macros as a convenient way of using a single source file that can be used both when debugging code in development and for final delivery, without having to edit and and edit out test/debug code.
I am doing it, in particular, in the context of using the compiler development tools flex and bison, but it would be equally valid when using just C.
For example, say some particular function returns a value:
The student may find it helpful to know what that value is. They could use an interactive debugger, in an IDE (such as Visual Studio), or a stand-alone debugger, such as gdb. Mentioning this makes them aware of the variety of development tools available to professional developers. I also mention that it can also be done without such tools, and code developers need to be aware of these wider varieties of debugging mechanisms. I indicate one of the primitive methods of doing is to add a
printf, such as this:
I show, using an example (in flex actually), how this can be helpful. I then explain some of the risks and weaknesses in doing this:
- We spawn two versions of the code, a production and debug version of the code
- We have to keep editing the prints in and out and may miss some
- We may make typos and not actually print the value being returned!
I then illustrate with using the preprocessor, which they may or may not have encountered:
printf("The Value is: %d\n",THIS_VALUE);
I then explain how this can be enabled by using flags in
gcc or other such tools. The next stage in the explanation shows what can happen when this debug coding happens in many many places:
printf("The Value is: %d\n",THIS_ONE_VALUE);
printf("The Value is: %d\n",THIS_TWO_VALUE);
printf("The Value is: %d\n",THIS_THREE_VALUE);
/* The code does not really look like this. I'm just making paradigms */
I point out how really poor this code is, and how it should be re-factored. This provides an opportunity for discussing re-factoring code and the options available. One option that will immediately occur to the student might be to use some form of procedure or function, like this:
int Trace(int The_Argument)
printf("The Value is: %d\n",The_Argument);
Students can see that this re-factoring may have improved the code, but now has inefficiencies that the original did not. It also has become type specific, as types were introduced for the first time (not quite true as the format is typed).
At this point Macros can be introduced as a neat and useful way of conditionally including debug code with the problems of editing statements in and out from the source. Something like this:
#define RETURN(x) printf("Returning " #x ": %d",x); return(x)
#define RETURN(x) return(x)
This then enables me to explain about the concepts of Macros versus functions. It also shows C stringification and initiates further discussion of these techniques.
I'm now ready to point them at some more professional discussions of the value of Macros, and in particular I like Jonathan Leffler's Stack Overflow answer.
I reinforce this by putting in the specification of their assignment that their code should implement certain specific debug flags which I will enable when assessing their work. They have to then master the technique to get grade points, in addition to having just working code.
Finally, now they know about Macros, I can talk about inline functions, how a Macro can be used to define one, based on the examples above. It also enables me to talk about compiler optimisations and how the compiler might introduce inlining itself without explicit macros from the programmer.
(This is part of a compilers course so such topics will be core knowledge for them).