Here's a simple little C program that had me confused for a while:
#include <stdio.h>
#define STR1(x) #x
#define STR(x) STR1(x)
int main(void) {
printf("%s\n", STR(MYDEF));
}
This just prints the value of the MYDEF #define as a string, using the standard stringizing double-define technique.
Compile (on Linux) with gcc -DMYDEF=abc prog.c run the result and, not surprisingly, it prints out 'abc'.
But change the value gcc -DMYDEF=linux prog.c and the result printed is not 'linux' but '1'.
So that confused me for a bit, but of course it happens because gcc (on Linux) has, I discovered, a built-in #define for the name 'linux' with a value '1', and the STR(x) macro ends up expanding MYDEF to 'linux' then linux to '1'.
In my real program (which was rather more complex than the little test above) I got round this by doing things in a different (probably better) way, but it left me curious ... is there a simple little macro technique that would avoid this double-substitution and make the program print out 'linux'? I know I could add a -U or #undef of linux, but that feels a bit clumsy.
I had thought all the built-in #defines start with underscores (usually double underscores), but I guess not.
There is no way to expand a macro only once, there's always a rescan performing further replacement (never recursive, of course). There are circumstances where macros aren't expanded at all (as with the # operator), which is why you need the extra replacement level with two #define like in your example.
In ISO C, identifiers without a leading underscore are free for you to use (not all of them, to be precise). The GNU C dialects define some other macros by default (like linux) for backwards compatibility, though they plan to remove such macros in the future.
To get a list of such macros on your machine, you can do:
$ echo | gcc -std=gnu99 -E -dM - | grep -v '# *define *_'
#define unix 1
#define linux 1
#define i386 1
With the options for ISO C (-ansi/-std=c89, -std=c99, -std=c11/-std=c1x for older Gcc), these macros are not defined:
$ cat test.c
#define STR1(x) #x
#define STR(x) STR1(x)
STR(MYDEF);
STR1(MYDEF);
$ gcc -std=gnu99 -DMYDEF=linux -E test.c
# 1 "test.c"
# 1 "<command-line>"
# 1 "test.c"
"1";
"MYDEF";
$ gcc -std=c99 -DMYDEF=linux -E test.c
# 1 "test.c"
# 1 "<command-line>"
# 1 "test.c"
"linux";
"MYDEF";
In ISO C mode, these macros properly are in the reserved namespace:
$ echo | gcc -std=c99 -E -dM - | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1
I see in the gcc manual that you can use the -ansi option to turn off predefined macros like "linux"
gcc -ansi -DMYDEF=linux prog.c
Related
The purpose of this is to build a program with command line-injected macros, using a Makefile.
I would like to define macros using multiple terms, however I am given an error as subsequent parts of the string are treated as files by gcc.
An example of what I need is as follows:
#define ULL unsigned long long
#define T_ULL typedef unsigned long long ull_t
As a result, I am only able to create macros that contain 1 term per definition.
The latter attempt allows me to create parameterized macros, however those are also limited to 1 term per definition.
Attempted solution
#include <stdio.h>
#define _STRINGIZE(x) #x
#define STRINGIZE(x) _STRINGIZE(x)
int main(void)
{
# ifdef DEBUG
# ifdef STRING
printf("%s", "A STRING macro was defined.\n");
printf("string: %s\n", STRINGIZE(STRING));
# else
printf("%s\n", "A DEBUG macro was defined.");
# endif
# endif
}
Results
As described by the man page, under the -D option description.
$ gcc define.c -D='DEBUG' ; ./a.out
A DEBUG macro was defined.
As described by this answer, as an alternative approach.
$ gcc define.c -D'DEBUG' ; ./a.out
A DEBUG macro was defined.
$ gcc define.c -D'DEBUG' -D'STRING="abc"' ; ./a.out
A STRING macro was defined.
string: "abc"
$ gcc define.c -D'DEBUG' -D'STRING="abc efg"' ; ./a.out
clang: error: no such file or directory: 'efg"'
A STRING macro was defined.
string: "abc"
$ gcc define.c -D'DEBUG' -D'STRING="abc efg hij"' ; ./a.out
clang: error: no such file or directory: 'efg'
clang: error: no such file or directory: 'hij"'
A DEBUG macro was defined.
string: "abc"
You don't need the STRINGIZE macro. The correct command-line syntax is:
gcc -DDEBUG -DSTRING='"abc def"' program.c
In other words, you need to quote the whole value of the defined macro, including C string delimiters (").
Then you can just do:
printf("string: %s\n", STRING);
I have a code base which uses #define in a different way then I am accustomed to.
I know that, for example, #define a 5 will replace variable a with 5 in the code.
But what would this mean:
'#define MSG_FLAG 5, REG, MSGCLR'
I tried doing it in a simple code and compiling it. It takes the last value (like the third argument as MSGCLR).
Preprocessing is largely just string replacement that happens before the "real" compilation starts. So we don't have any idea of what a variable is at this point.
The commas here are not any special syntax. This will cause any appearance of MSG_FLAG in the code to be replaced by 5, REG, MSGCLR
Most compilers have a flag that will just run the preprocessor, so you can see for yourself. On gcc, this is -E.
So to verify this, we can have some nonsense source:
#define MSG_FLAG 5, REG, MSGCLR
MSG_FLAG
Compile with gcc -E test.c
And the output is:
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.c"
5, REG, MSGCLR
I want to clear a false positive on FD_ZERO and FD_SET when the memory sanitizer is in use. Clearing it is somewhat easy:
#include <sanitizer/msan_interface.h>
...
__msan_unpoison(&readfds, sizeof(readfds));
__msan_unpoison(&writefds, sizeof(writefds));
However, I don't know how to detect when the memory sanitizer is in use. That is, detect when -fsanitize=memory was specified on the command line. The preprocessor does not seem to be helping:
$ clang -dM -E -fsanitize=memory - </dev/null | egrep -i 'memory|sanitize|msan'
$
How can I determine when -fsanitize=memory is in use?
According to Konstantin Serebryany on the Memory Sanitizer mailing list, there is no preprocessor macro. The __has_feature(memory_sanitizer) should be used:
#if defined(__has_feature)
# if __has_feature(memory_sanitizer)
# define MEMORY_SANITIZER 1
# endif
#endif
...
#ifdef MEMORY_SANITIZER
# include <sanitizer/msan_interface.h>
#endif
...
#ifdef MEMORY_SANITIZER
__msan_unpoison(&readfds, sizeof(readfds));
__msan_unpoison(&writefds, sizeof(writefds));
#endif
...
I'm attempting to refactor a piece of legacy code and I'd like a snapshot of all of the macros defined at a certain point in the source. The code imports a ridiculous number of headers etc. and it's a bit tedious to track them down by hand.
Something like
#define FOO 1
int myFunc(...) {
PRINT_ALL_DEFINED_THINGS(stderr)
/* ... */
}
Expected somewhere in the output
MACRO: "FOO" value 1
I'm using gcc but have access to other compilers if they are easier to accomplish this task.
EDIT:
The linked question does not give me the correct output for this:
#include <stdio.h>
#define FOO 1
int main(void) {
printf("%d\n", FOO);
}
#define FOO 0
This very clearly prints 1 when run, but gcc test.c -E -dM | grep FOO gives me 0
To dump all defines you can run:
gcc -dM -E file.c
Check GCC dump preprocessor defines
All defines that it will dump will be the value defined (or last redefined), you won't be able to dump the define value in all those portions of code.
You can also append the option "-Wunused-macro" to warn when macros have been redefined.
We can use the output of a command (with flags) as macro pre-procesor (eg. uname --kernel-name)
#include <stdio.h>
#define version(v) #v
#define VERSION(v) version(v)
int main(void)
{
printf("Version: %s\n", VERSION(kernel_name));
return 0;
}
Compiled using:
gcc -Wall -pedantic -o demo demo.c -Dkernel_name=$(uname --kernel-name)
Output:
Version: Linux
But this fail when macro begins with # (eg. uname --kernel-version):
david#debian:~$ uname --kernel-version
#1 SMP Debian 3.2.46-1+deb7u1
Because you can not define a preprocessor starting with #
#define SOMETHING #something /* error: stray ‘#’ in program */
Is there way to quote the output of command?
"#1 SMP Debian 3.2.46-1+deb7u1"
in order to get a valid input?
Easy.
Replace
-Dkernel_name=$(uname --kernel-name)
with
-Dkernel_name="\"$(uname --kernel-name)\""
This works because the -D flag is equivalent to a define. So -Dvar=val is equivalent to having a #define var val in your source.
When you do -Dkernel_name="\"$(uname --kernel-name)\"", your shell converts the define option value to equal "#1 SMP Debian 3.2.46-1+deb7u1" - one set of quotes for your shell and another for your actual program. This saves you from having to stringify too.