1

When can I pass variable's value to a macro for stringifying?

For example the code taken from this post works with a constant-defined macro.

#define MAX_STRING_LENGTH 20 #define STRINGIFY(x) STRINGIFY2(x) #define STRINGIFY2(x) #x { ... char word[MAX_STRING_LENGTH+1]; scanf("%" STRINGIFY(MAX_STRING_LENGTH) "s", word); ... } 

However I cannot use it with a variable such as:

{ ... int val = 20; char word[MAX_STRING_LENGTH+1]; scanf("%" STRINGIFY(val) "s", word); ... } 

since the compilation is successful with this warning:

warning: invalid conversion specifier 'v' [-Wformat-invalid-specifier] scanf("%" STRINGIFY(var) "s", word); ~~~^~~~~~~~~~~~~ test2.c:4:22: note: expanded from macro 'STRINGIFY' #define STRINGIFY(x) STRINGIFY2(x) ^ test2.c:5:23: note: expanded from macro 'STRINGIFY2' #define STRINGIFY2(x) #x ^ <scratch space>:466:2: note: expanded from here "var" ^ 1 warning generated 

but the run of the code does not wait for any input.

On the contrary in this other post it was possible to pass a variable to this macro:

#define PRINT(int) printf(#int "%d\n",int) ... int var =8; PRINT(var); 

What is the difference between the two cases? How can I modify the first one so that it accepts also variables?

I tried using %d inside the macro but I was not successful.

2 Answers 2

5

The preprocessor always operates on tokens only.

A macro is not a function. You don't pass it a variable (by value). You pass a token sequence. In STRINGIFY(MAX_STRING_LENGTH) the token sequence is MAX_STRING_LENGTH, and in STRINGIFY(val) it's the token sequence val.

MAX_STRING_LENGTH is itself a macro, and due to how STRINGIFY is defined to work, the macro will be expanded by the preprocessor before turning it a string literal. So 20 is in turn the token which gets # applied to it, and it produces "20" as a string literal.

On the other hand val is not a macro, the preprcosseor is not going to expand it. It's going to keep the token sequence as val. The fact val is the name of a variable with some value means nothing to the preprocessor, it only cares about tokens. So val is transformed into the literal "val".

The example you brought from another post worked because it expanded to this:

printf("var" "%d\n", var); 

The variable name in #int turns into a literal, there is no magic that lets the preprocessor read a variable's value. The fact var 8 is printed is only because var is passed as an argument to printf! It's printed at run-time by the %d specifier.

Finally, when experimenting with the preprcoessor it's always helpful to look at the source file after prpeprocessing is done, but before the file is compiled. The gcc -E flag (or equivalent for your compiler) can help you do that.

Sign up to request clarification or add additional context in comments.

18 Comments

Thanks for your answer. Is there/do you know any work-around with macros to generate a string in the form %20s (something like maybe "%%%ds", var) to be passed then to a function like scanf?
@StoryTeller But that's for printf only, scanf does not support this (unfortunately!)...
@StoryTeller Oh, need to admit, overlooked that missing period myself, too... But as far as I interprete the question, that would be exactly what OP seeks for: scanf("%.*s", val, word); - which actually is what I have missed myself so often, too...
@FrancescoBoi No, it doesnt't, at least not with standard C.
@FrancescoBoi - Nope. Scanf does not support dynamic buffer length specification. If you are on a POSIX system then you could use %ms. That will use malloc internally to allocate the buffer (and you need to pass the address of a pointer to assign to), but it's POSIX only, again.
|
3

STRINGIFY(val) will result in "val", not the value you wanted to stringify, so you get a final format string of "%vals" ("%" "val" "s"). That's how the C preprocessor works, it does just text replacements, nothing more.

The PRINT example:

#define PRINT(int) printf(#int "%d\n", int) PRINT(var); // to be resolved printf(#var "%d\n", var); // intermediate result printf("var" "%d\n", var); // final result, this is what the C compiler sees 

But why did it work with MAX_STRING_LENGTH?

#define MAX_STRING_LENGTH 20 #define STRINGIFY(x) STRINGIFY2(x) #define STRINGIFY2(x) #x STRINGIFY(MAX_STRING_LENGTH) // to be resolved STRINGIFY2(20) // intermediate step; STRINGIFY2 known as macro, thus: #20 // another intermediate step "20" // final result 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.