C macros

 

This page is about the macros that are included in a programme using the "#define" pre-processing directive - for example -


        #define MACRO_1 2010

In their basic concept, macros should be quite simple - the macro name - ie, MACRO_1 is used to represent some text - the text may be a number, a block of code, or even another macro. During the pre-processing, the compiler puts in the text wherever the macro has been used, and the compiler just sees the text, it doesn`t know the macro was ever there.

However using macros can actually be quite difficult, as there are loads of different ways that the substitution doesn`t go according to the intended plan, and either the compilation will generate an error message, or the code compiles okay, but the programme doesn`t run the way it should.

What follows is some of the different ways to use macros.

Most of the information in this page came from books and websites. These sources may be correct from where their author is sitting, but I`ve subsequently found that GNU compiler gcc behaves quite differently in certain respects. So I have added some comments about that.

Just as an aside, I have seen reference to the fact that the pre-compiler doesn`t know about C keywords, so you can use C keywords as macro names. During the pre-compilation, they are all removed anyway, so the compiler doesn`t know they were ever there. However using C keywords as macro names would probably make the source code script a bit more confusing to read, so it probably isn`t good practice.

To a certain extent, it seems to work. However if the keyword that you have used as a macro name is subsequently used in the script as a keyword, then it gets replaced, and the compilation fails. So not only would it be bad practice, it`s a bad idea !

 

Simple macros

These are the easiest type of macros to understand - the example shown above is a simple macro - the macro MACRO_1 is used throughout the programme, and whenever the pre-compiler meets MACRO_1, it puts in the number 2010.

Next year, we can just change this one line of code to 2011, we don`t have to search through the code looking for all the 2010`s.

It is good practice to use uppercase letters for the names of macros, so they stand out in the source code.

A simple macro is also known as an object-like macro. The most commom use is to give symbolic names to numeric constants, just like the example above.

Normally, a macro should contain a single line of text, although if the text is a string or a character constant, it can contain newline characters.

If there are any comments in the text - ie, text enclosed in /*........*/, then the pre-compiler removes all the comments, and replaces them with a unit of whitespace per comment.

Macro definitions become effective from wherever they are defined in the source code.

The macro text can be another macro -


        #define MACRO_1 2010

        #define MACRO_2 MACRO_1

This is known as cascaded macros.

The above appears to work for gcc, as long as gcc sees the replacement characters as an integer. If the replacement characters are text, then they have to be enclosed in double quotes, or the compilation fails.

It appears that gcc is not just treating a macro as a way of doing character substitution during the pre-compilation process, because trying to do a character substitution within the prinf() fuction doesn`t work. The substitution causes a compilation error.

However, if you treat a macro like a variable within a printf() statement, then it does do a substitution. So in some way, gcc isn`t doing straight character substitution, but it is treating the macro as a live item, for subsequent use. Whether this is something unique to gcc, I don`t know. It doesn`t agree with what I`ve read about macros.

 

Function-like macros

Macros can be defined in a way so that they are a bit like functions - they are defined like simple macros, but a pair of parenthesis are placed immediately after the macro name - with no space.


        #define MACRO_1() 2010

If the source code script uses the macro name with the brackets, then the substitution is done. If the source code script uses the macro name without the brackets, then the substitution does not get done.

It is important that there is no space between the macro name and the parenthesis - if a space is put there, the pre-compiler assumes that this is a simple macro, and whatever follows the macro name is the substitute text.

With gcc, if you try to use the macro name without the brackets, the compilation fails. It looks as if gcc sees the brackets as part of the macro name, however that may not be the case.

 

Function-like macros with arguments

Function-like macros can be defined with arguments, like an actual function, and the substitution depends on the pre-compiler having to do something, like arithmetic operations.


        #define MACRO_3(x,y) x + y

There are four parts to this -

Again, it is important that there is no space between the macro name and the parenthesis containing the parameters - if a space is put there, the pre-compiler assumes that this is a simple macro, and whatever follows the macro name is the substitute text.

Parenthesis are useful to overcome unwanted operator precedence. For example -


        #define MACRO_4(x,y,z) (x + y) * z

Without the parenthesis, the pre-compiler would perform the operation of multiplying y and z, then add on the x. This is one of the ways that using macros can cause a problem with the end progamme, rather than with the compilation.

It is important that every parameter used in the operational part of the macro definition is also shown in the parameter part of the macro definition.

Macro parameters that appear in the argument in quotes - ie, string literals, are not substituded. So


        #define MACRO_4(x) "x"

would appear as "x" after pre-compilation.

The parameters in these macros don`t have to be single characters like x, y, z - they can be combinations of characters that are maybe text, other macro names, function names, etc.

This seems to be somewhat thwarted in gcc, because gcc needs the double quotes anyway.

 

Stringification

In stringification, the macro substitution in the pre-compiler turns the argument into a text string in quotes.


        #define MACRO_5(text) #text

After pre-compilation, the macro MACRO_5 will be replaced with "text", so the argument has become a text string.

Whitespace before and after the text to be stringified is ignored. Comments are changed to a single space. Multiple spaces inside the text are replaced with one space.

Trying to stringify some text that is quoted will result in the pre-compiler escaping the quotes with backslashes. So


        #define MACRO_5(text) #"text"

will come out as


        "\"text\""

Likewise, backslash characters in the text are also escaped -


        #define MACRO_5(text) #text\text

will come out as


        "text\\text"

If you want to stringify the expansion of a macro, you need to use two macros - one is used to substitute the text, and the other is used to stringify the results of the expansion.

gcc doesn`t really comply with much in this section - for a character string without any spaces, the # character can be used instead of double quotes, and you get the text displayed. If you have spaces in the text string, the compilation fails. If you have the # and double quotes, the compilation fails.

It still looks as if the printf function sees the parameters inside the brackets as part of the macro name. However one of the compilation error messages did actually refer to the parameters, so at present, I am not sure what is going on.

 

Concatenation

This is a way of stringifying two bits of text into one longer bit of text using ##. So


        #define DIR_NAME(firstname,surname) firstname_##surname

will produce the result


        "firstname_surname"

If firstname and surname are variables, this provides a way of producing full names. In theory !

gcc continues to be idiosyncratic - it will not compile unless the substitution text is in double quotes, and if it is in double quotes, gcc just sees the whole substitution text as a text string, including the ##.

 

Undefining a macro

Pretty much as you would expect, this is a way to undefine a macro. Once it`s gone it`s gone !


        #undef MACRO-5

Once a macro has been undefined, the same name can be used again, even for a macro of a completely different type.

Well for once gcc behaved correctly.

 

Redefining a macro

Redefining a macro uses the #define command again. If the old definition and the new definition are effectively the same, then the pre-compiler will actually ignore the redefinition. If there are effective differences, then the pre-compiler will issue a warning, but then go ahead with the redefinition.

And again, gcc behaved correctly, it compiled, with a warning, and with a statement about where the previous definition was.

 

Predefined macros

These are macros that are predefined within C. There are quite a few of them, and what they are depend on the version of C.

The following ones are those that are defined for ANSI C.

GNU C adds another 4 -

GNU C lists another 135 macros, which it lists as extensions. Some sources list another 12 machine specific macros, which don`t use the double underscores to delineate them, and their names are in lower case. So there are a lot more out there.

 

Back to the CF8/TT8

In the previous page, I mentioned a typical embedded use of C on the CF8/TT8 combination micro-controller boards. The CF8 requires the definition of 3 CF8 specific macros in the C script, as well as the various header files required by the CF8/TT8. So the start of the C source code script is maybe going to look something like -


        #include <tt8.h>

        #include <tt8lib.h>

        #include <dio332.h.h>

        #include <stdio.h>

        #include <math.h>

        #include <time.h>

        #include <userio.h>

        #include <string.h>

        #include <PicoDOS8.h>

        #include <PicoDCF8.h>

        #define CF8StdCS       2

        #define CF8StdAddr     0x800000

        #define InitCF8(cs,ad) \.....

There is quite a lot of guesswork in that, so it is only an indication of what might be there.

 

After all that ...

So which is right ?

gcc behaves differently with strings, it appears that it doesn`t do text substitution the way it should, and many of the above descriptions just don`t work.

At the moment, I have no idea !

 

 

 

 

 

website design by ron-t

 

website hosting by freevirtualservers.com

 

© 2024   Ron Turner

 

+                                   +  

 

Link to the W3C website.   Link to the W3C website.