Getting started: using existing macros#
Before defining our own macros, let’s see how they are used.
Constants#
The most basic usage of macros is 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 assignation 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 assignation 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 assignation 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 you can see in the preprocessed tab, those constants are evaluated before compilation, and replaced by plain literals:
Macros used in the example
Macro |
Expansion |
---|---|
|
|
|
|
|
|
|
|
|
|
Debugging constants#
Speaking of existing macro constants we can use, there exist certain macro constants, whose value is not always the same… 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
Additionnally, the GNU C extension include:
__FILE_NAME__
similar to__FILE__
but only includes the file name, excluding the path
While we’re at it, let’s also mention:
__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
They 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:
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 |