Solution
You can make a little Preprocessor hack:
#define MAX_BUFFER 30
#define FORMAT(s) "%" #s "s"
#define FMT(s) FORMAT(s)
int main(void)
{
char buffer[MAX_BUFFER + 1];
scanf(FMT(MAX_BUFFER), buffer);
printf("string: %s\n", buffer);
printf("length: %d\n", strlen(buffer));
return 0;
}
The FORMAT
and FMT
macros are necessary for the preprocessor to translate them correctly. If you call FORMAT
directly with FORMAT(MAX_BUFFER)
, it will translate into "%" "MAX_BUFFER" "s"
which is no good.
You can verify that using gcc -E scanf.c
. However, if you call it through another macro, which will effectively resolve the macro names for you and translate to "%" "30" "s"
, which is a fine format string for scanf
.
Edit
As correctly pointed out by @Jonathan Leffler in the comments, you can't do any math on that macro, so you need to declare buffer
with plus 1 character for the NULL terminating byte, since the macro expands to %30s
, which will read 30 characters plus the null byte.
So the correct buffer declaration should be char buffer[MAX_BUFFER + 1];
.
Requested Explanation
As asked in the comments, the one macro version won't work because the preprocessor operator #
turns an argument into a string (stringification, see bellow). So, when you call it with FORMAT(MAX_BUFFER)
, it just stringifies MAX_BUFFER
instead of macro-expanding it, giving you the result: "%" "MAX_BUFFER" "s"
.
Section 3.4 Stringification of the C Preprocessor Manual says this:
Sometimes you may want to convert a macro argument into a string constant. Parameters are not replaced inside string constants, but you can use the ‘#’
preprocessing operator instead. When a macro parameter is used with a leading ‘#’
, the preprocessor replaces it with the literal text of the actual argument, converted to a string constant. Unlike normal parameter replacement, the argument is not macro-expanded first. This is called stringification.
This is the output of the gcc -E scanf.c
command on a file with the one macro version (the last part of it):
int main(void)
{
char buffer[30 + 1];
scanf("%" "MAX_BUFFER" "s", buffer);
printf("string: %s\n", buffer);
printf("length: %d\n", strlen(buffer));
return 0;
}
As expected. Now, for the two levels, I couldn't explain better than the documentation itself, and in the last part of it there's an actual example of this specific case (two macros):
If you want to stringify the result of expansion of a macro argument, you have to use two levels of macros.
#define xstr(s) str(s)
#define str(s) #s
#define foo 4
str (foo)
==> "foo"
xstr (foo)
==> xstr (4)
==> str (4)
==> "4"
s
is stringified when it is used in str
, so it is not macro-expanded first. But s
is an ordinary argument to xstr
, so it is completely macro-expanded before xstr
itself is expanded (see Argument Prescan). Therefore, by the time str
gets to its argument, it has already been macro-expanded.
Resource