Predefined Macros in C
In addition to user-defined macros, the C standard defines some predefined macros that can be used in a C program.
These macros provide information about the program at compile time, such as the date and time of compilation, the source file name and line number, the conformance to the C standard and the adopted standard version.
In this lesson we will see an overview, obviously not exhaustive, of the most common predefined macros in C language.
Predefined Macros
All C compilers provide a set of predefined macros that can be used in a C program. These macros are defined by the compiler and cannot be modified.
Typically, these macros represent numeric constants or string literals.
Macros __DATE__ and __TIME__
These two macros return the current date and time when the program was compiled.
In particular, __DATE__ contains the date in the format Mmm dd yyyy of the day when the program was compiled, while __TIME__ contains the time in the format hh:mm:ss.
These two macros are useful for including information about the date and time of compilation within the program and, therefore, to be able to discriminate different versions of the same program.
For example, we can use these two macros in our code as follows:
/* ... */
printf("Example Program\n");
printf("Compiled on %s at %s\n", __DATE__, __TIME__);
/* ... */
By trying to compile and run the program, we get an output similar to this:
Example Program
Compiled on Mar 12 2022 at 14:34:21
Macros __DATE__ and __TIME__
__DATE__: Predefined macro that returns the compilation date of the program in the formatMmm dd yyyy.__TIME__: Predefined macro that returns the compilation time of the program in the formathh:mm:ss.
Macros __FILE__ and __LINE__
The predefined macros __FILE__ and __LINE__ return the name of the source file and the line number where they are located respectively.
One of the fundamental uses of these two macros is debugging and logging of the code.
In the first case, they are used to identify the point of a program that caused an error. We can, in fact, use these two macros to print the file name and line number where an error occurred.
/* ... */
if (error_condition) {
printf("Error in file %s at line %d\n", __FILE__, __LINE__);
}
/* ... */
In this case, if the condition error_condition occurs, the program will print the file name and line number where the error occurred. For example:
Error in file main.c at line 42
Another important use is logging. When the program performs a certain action or executes a certain operation, we can print the file name and line number where the action occurred.
/* ... */
Operation1();
printf("Operation1 completed in file %s at line %d\n", __FILE__, __LINE__);
Operation2();
printf("Operation2 completed in file %s at line %d\n", __FILE__, __LINE__);
/* ... */
In this case, the program will print the file name and line number where the operation was completed. For example:
Operation1 completed in file main.c at line 42
Operation2 completed in file main.c at line 45
Macros __FILE__ and __LINE__
__FILE__: Predefined macro that returns the name of the source file.__LINE__: Predefined macro that returns the line number where the macro is located.
Macros __STDC__ and __STDC_VERSION__
The predefined macro __STDC__ is used to verify if the C compiler conforms to the C standard. Nowadays it is rarely used, since if a compiler does not conform to the standard it means that it does not adopt the C89 standard and therefore it is an obsolete compiler.
In any case, it is worth 1 if the compiler conforms to the C standard, otherwise it is undefined.
/* ... */
if (__STDC__) {
printf("The compiler conforms to the C standard\n");
} else {
printf("The compiler does not conform to the C standard\n");
}
We will see a possible use of it in the lesson on conditional compilation.
Directly related to the macro __STDC__ is the macro __STDC_VERSION__, which returns the version of the C standard adopted by the compiler.
In particular, if the macro __STDC__ is defined, and therefore the compiler conforms to the standard, the macro __STDC_VERSION__ will return a different value depending on the adopted standard. In particular, its values will be:
| Value | C Standard |
|---|---|
199409L |
C89 |
199901L |
C99 |
201112L |
C11 |
201710L |
C17 |
In particular, the values represent the year and month when the standard was ratified.
Macros __STDC__ and __STDC_VERSION__
__STDC__: Predefined macro that is worth1if the compiler conforms to the C standard, otherwise it is undefined.__STDC_VERSION__: Predefined macro that returns the version of the C standard adopted by the compiler in the form of year and month.
Macro __STDC_HOSTED__
To understand what the macro __STDC_HOSTED__ does, we must first understand what a hosted and freestanding environment is.
When we talk about C language implementation we mean the set of compiler, libraries and additional software necessary for the execution of a C program.
Starting from the C99 standard, the C language defines two types of implementations:
-
Hosted: a hosted implementation is an implementation that provides a complete environment for the execution of a C program. This environment includes standard libraries, an operating system and a user interface.
The constraint imposed by the standard is that any hosted implementation must be able to compile and execute a program written in standard C language.
-
Freestanding: a freestanding implementation is an implementation that does not provide a complete environment. It is not certain that the libraries and header files of the C standard are present. An implementation of this type is often used for embedded systems, for example microcontrollers such as PICs, AVRs or Arduino.
The macro __STDC_HOSTED__ is used to verify if the C compiler is able to compile a hosted or freestanding program. In the first case, the macro is worth 1, otherwise it is undefined.
Macro __STDC_HOSTED__
The macro __STDC_HOSTED__, defined starting from the C99 standard, is worth 1 if the C compiler is able to compile a hosted program, otherwise it is undefined.
Other Standard Macros
In addition to the predefined macros we have seen, the C99 standard defines other macros that can be useful in certain situations.
For example, we can mention:
__STDC_IEC_559__: is worth1if the compiler supports the IEC 60559 floating-point format (known as IEEE 754).__STDC_IEC_559_COMPLEX__: is worth1if the compiler supports the IEC 60559 floating-point format for complex numbers.__STDC_ISO_10646__: is worth1if the compiler supports the ISO 10646 character encoding format.
Similarly, since the C11 standard introduces new features, new predefined macros have been introduced to verify their presence:
__STDC_NO_THREADS__: is worth1if the compiler does not support threads.__STDC_NO_ATOMICS__: is worth1if the compiler does not support atomic operations.
These two macros concern the new features introduced by the C11 standard for concurrent programming that we will see in the next lessons.
Identifier __func__
We conclude this lesson on predefined macros with one last special identifier introduced in C99: __func__.
The identifier __func__ is not a macro, so in reality it should be addressed in another lesson. However, its use is always combined with the macros __FILE__ and __LINE__, so it seemed appropriate to treat it in this lesson.
Each C function, including the main function, has access to this special identifier that stores the name of the function currently being executed. At a practical level, it is as if the compiler, at the beginning of each function, declared a constant static variable in this way:
static const char __func__[] = "function_name";
Where function_name is the name of the current function.
It is not a macro, but its use is similar to that of the macros __FILE__ and __LINE__, and is widely used for debugging and logging.
We can, for example, define two macros for logging:
#define FUNCTION_ENTRY() printf("Entry in %s\n", __func__)
#define FUNCTION_EXIT() printf("Exit from %s\n", __func__)
And use them within our functions:
void function() {
FUNCTION_ENTRY();
/* ... */
FUNCTION_EXIT();
}
In this way, the program will print the name of the function being entered and exited from:
Entry in function
Exit from function
Another possible use is to pass __func__ to a function, so that the called function knows the name of the calling function.
void logging_function(const char *function_name) {
printf("Called from %s\n", function_name);
}
void function() {
logging_function(__func__);
}
In this case, the function logging_function will print the name of the calling function:
Called from function
Identifier __func__
The identifier __func__ is not a macro, but a constant static variable that stores the name of the function currently being executed.
In Summary
This lesson was useful to show an overview, not exhaustive, of the predefined macros in C language. These macros are useful for obtaining information about the program at compile time, such as the date and time of compilation, the source file name and line number, the conformance to the C standard and the adopted standard version.
Often, many C compilers also provide additional macros that can be useful in certain situations. In these cases it is always advisable to refer to the compiler manuals to have detailed information on the supported macros.
Finally, we have seen the identifier __func__, which is not a macro, but a constant static variable that stores the name of the function currently being executed. This identifier is very useful for debugging and logging of the code.
In the next lesson, we will study macros with variable number of parameters, one of the new features introduced by the C99 standard.