Getting started: using existing macros#
Before writing our own macros, it helps to see what using them looks like. This chapter surveys macros that require no external dependencies: constants from the standard library and special macros provided by the compiler.
Constants#
The most basic use of macros is as constants:
1#include <stdio.h> // dprintf
2#include <stdlib.h> // atoi, NULL, EXIT_*
3#include <string.h> // strchr
4#include <unistd.h> // *_FILENO
5
6/* Output the environment as a markdown table */
7int main(int arg_count, char** arg_values, char** environment)
8{
9 char* equal;
10 unsigned max = arg_count > 1 ? atoi(arg_values[1]) : 7;
11
12 dprintf(STDOUT_FILENO, "__Environment__:\n| Name | Value |\n|:-|:-|\n");
13 do
14 {
15 if ((equal = strchr(*environment, '=')) == NULL)
16 {
17 dprintf(STDERR_FILENO, "Expected an assignment but got \"%s\"\n", *environment);
18 return EXIT_FAILURE;
19 }
20 *equal = '\0';
21 dprintf(STDOUT_FILENO, "|`%s`|`%1.30s`|\n", *environment, equal + 1);
22 } while (--max > 0 && *++environment);
23 return EXIT_SUCCESS;
24}
1#include <stdio.h> // dprintf
2#include <stdlib.h> // atoi, NULL, EXIT_*
3#include <string.h> // strchr
4#include <unistd.h> // *_FILENO
5
6/* Output the environment as a markdown table */
7int main(int arg_count, char** arg_values, char** environment)
8{
9 char* equal;
10 unsigned max = arg_count > 1 ? atoi(arg_values[1]) : 7;
11
12 dprintf(STDOUT_FILENO, "__Environment__:\n| Name | Value |\n|:-|:-|\n");
13 do
14 {
15 if ((equal = strchr(*environment, '=')) == NULL)
16 {
17 dprintf(STDERR_FILENO, "Expected an assignment but got \"%s\"\n", *environment);
18 return EXIT_FAILURE;
19 }
20 *equal = '\0';
21 dprintf(STDOUT_FILENO, "|`%s`|`%1.30s`|\n", *environment, equal + 1);
22 } while (--max > 0 && *++environment);
23 return EXIT_SUCCESS;
24}
/* Output the environment as a markdown table */
int main(int arg_count, char** arg_values, char** environment)
{
char* equal;
unsigned max = arg_count > 1 ? atoi(arg_values[1]) : 7;
dprintf(1 /* Standard output. */, "__Environment__:\n| Name | Value |\n|:-|:-|\n");
do
{
if ((equal = strchr(*environment, '=')) == ((void*)0))
{
dprintf(2 /* Standard error output. */,
"Expected an assignment but got \"%s\"\n",
*environment);
return 1 /* Failing exit status. */;
}
*equal = '\0';
dprintf(1 /* Standard output. */, "|`%s`|`%1.30s`|\n", *environment, equal + 1);
} while (--max > 0 && *++environment);
return 0 /* Successful exit status. */;
}
Environment:
Name |
Value |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
As shown in the preprocessed tab, these constants are evaluated before compilation, and replaced by plain literals:
Macros used in the example
Macro |
Expansion |
|---|---|
|
|
|
|
|
|
|
|
|
|
Debugging constants#
Some macro constants are special: their value changes depending on where in the source they appear. Such behavior can only be achieved from within the compiler, and we will not be able to create our own.
The C standard requires compilers to define certain special macros, without the need to include any header, including:
__FILE__expands to the name of the file currently compiled, as a C string literal__LINE__expands to the line number currently compiled, as an integer literal
Source: GNU
Additionally, the GNU C extension include:
__FILE_NAME__similar to__FILE__but only includes the file name, excluding the path
Related compiler-provided identifiers include:
__FUNCTION__(also__func__) is a magic constant character array that contains the name of the current function__PRETTY_FUNCTION__is similar but includes the whole signature, including return type and parameters
Warning
__FUNCTION__ and __PRETTY_FUNCTION__ are not macros, but runtime constants. As the GNU documentation puts it:
These identifiers are variables, not preprocessor macros, and may not be used to initialize char arrays or be concatenated with string literals.
Source: GNU
1#include <stdbool.h> // bool
2#include <stdio.h> // printf
3
4void foo(void)
5{
6 printf("%s@%s:%i\n", __FILE_NAME__, __FUNCTION__, __LINE__);
7}
8
9bool bar(int arg)
10{
11 printf("In file %s in function %s at line %i: arg=%i\n", __FILE__, __PRETTY_FUNCTION__, __LINE__, arg);
12 return true;
13}
14
15int main()
16{
17 foo();
18 bar(42);
19}
1#include <stdbool.h> // bool
2#include <stdio.h> // printf
3
4void foo(void)
5{
6 printf("%s@%s:%i\n", __FILE_NAME__, __FUNCTION__, __LINE__);
7}
8
9bool bar(int arg)
10{
11 printf("In file %s in function %s at line %i: arg=%i\n", __FILE__, __PRETTY_FUNCTION__, __LINE__, arg);
12 return true;
13}
14
15int main()
16{
17 foo();
18 bar(42);
19}
void foo(void)
{
printf("%s@%s:%i\n", "02_debug.c", __FUNCTION__, 6);
}
_Bool bar(int arg)
{
printf("In file %s in function %s at line %i: arg=%i\n",
"samples/02_debug.c",
__PRETTY_FUNCTION__,
11,
arg);
return 1;
}
int main()
{
foo();
bar(42);
}
02_debug.c@foo:6
In file samples/02_debug.c in function _Bool bar(int) at line 11: arg=42
Function-like#
Macros can also take parameters. Unlike functions, they expand at compile-time: there is no call overhead and no type constraints:
1#include <termios.h> // struct termios
2
3#include <stddef.h> // offsetof
4#include <stdio.h> // printf
5
6#if __has_include("linux/stddef.h")
7# include <linux/stddef.h> // sizeof_field
8#endif
9#ifndef sizeof_field
10# define sizeof_field(Type, Member) sizeof(((Type){}).Member)
11#endif
12
13/** Learn about the memory layout of the termios struct */
14int main()
15{
16 printf("__Termios structure__:\n| Name | Position | Size |\n|:-|-:|-:|\n");
17 printf("|`c_iflag`|%zu|%zu|\n",
18 offsetof(struct termios, c_iflag),
19 sizeof_field(struct termios, c_iflag));
20 printf("|`c_cflag`|%zu|%zu|\n",
21 offsetof(struct termios, c_cflag),
22 sizeof_field(struct termios, c_cflag));
23 printf("|`c_lflag`|%zu|%zu|\n",
24 offsetof(struct termios, c_lflag),
25 sizeof_field(struct termios, c_lflag));
26 printf("|`c_cc`|%zu|%zu|\n",
27 offsetof(struct termios, c_cc),
28 sizeof_field(struct termios, c_cc));
29}
1#include <termios.h> // struct termios
2
3#include <stddef.h> // offsetof
4#include <stdio.h> // printf
5
6#if __has_include("linux/stddef.h")
7# include <linux/stddef.h> // sizeof_field
8#endif
9#ifndef sizeof_field
10# define sizeof_field(Type, Member) sizeof(((Type){}).Member)
11#endif
12
13/** Learn about the memory layout of the termios struct */
14int main()
15{
16 printf("__Termios structure__:\n| Name | Position | Size |\n|:-|-:|-:|\n");
17 printf("|`c_iflag`|%zu|%zu|\n",
18 offsetof(struct termios, c_iflag),
19 sizeof_field(struct termios, c_iflag));
20 printf("|`c_cflag`|%zu|%zu|\n",
21 offsetof(struct termios, c_cflag),
22 sizeof_field(struct termios, c_cflag));
23 printf("|`c_lflag`|%zu|%zu|\n",
24 offsetof(struct termios, c_lflag),
25 sizeof_field(struct termios, c_lflag));
26 printf("|`c_cc`|%zu|%zu|\n",
27 offsetof(struct termios, c_cc),
28 sizeof_field(struct termios, c_cc));
29}
/** Learn about the memory layout of the termios struct */
int main()
{
printf("__Termios structure__:\n| Name | Position | Size |\n|:-|-:|-:|\n");
printf("|`c_iflag`|%zu|%zu|\n",
__builtin_offsetof(struct termios, c_iflag),
sizeof(((struct termios){}).c_iflag));
printf("|`c_cflag`|%zu|%zu|\n",
__builtin_offsetof(struct termios, c_cflag),
sizeof(((struct termios){}).c_cflag));
printf("|`c_lflag`|%zu|%zu|\n",
__builtin_offsetof(struct termios, c_lflag),
sizeof(((struct termios){}).c_lflag));
printf("|`c_cc`|%zu|%zu|\n",
__builtin_offsetof(struct termios, c_cc),
sizeof(((struct termios){}).c_cc));
}
Termios structure:
Name |
Position |
Size |
|---|---|---|
|
0 |
4 |
|
8 |
4 |
|
12 |
4 |
|
17 |
32 |
With the compiler-provided constants in hand, the next chapter puts them to use in a logging utility.
Recap#
This chapter covered:
Macros used as constants are replaced by their value before compilation — no runtime cost, no linking required
Some compiler-provided macros (
__FILE__,__LINE__) change value depending on where they appear in the source__func__is not a macro but a runtime constant — it cannot be concatenated with string literalsFunction-like macros take parameters and expand at compile-time, with no type constraints