How to use a define inside a format string? - c

Say I have a character array:
#define LEN 10
char arr[LEN + 1];
Lets do some scanf operation to it:
scanf("Name: %s", arr);
This could be dangerous if someone is typing a name that is longer than 10 characters. So better use this:
scanf("Name: %10s", arr);
Well now I would run into trouble if LEN is changed. I would have to go through the whole code to correct every line where I used the 10 in context of arr. So I thought about somehting like this:
scanf("Name: %LENs", arr);
But this will not work.LEN is not resolved by the preprocessor beacuse it is used inside a string.
How to use a define inside a format string?

C joins adjacent string literals and you can stringify a preprocessor parameter with #, so the following should do the trick:
#define LEN 10
// this converts to string
#define STR_(X) #X
// this makes sure the argument is expanded before converting to string
#define STR(X) STR_(X)
[...]
scanf("Name: %" STR(LEN) "s", arr);
The macros are needed because with just #LEN, you'd end up with LEN expanded to 10, and with only one macro applying # to its argument, the result would be "LEN" (the argument wouldn't be expanded).
The preprocessor / compiler will transform this in the following steps:
1. scanf("Name: %" STR_(10) "s", arr);
2. scanf("Name: %" "10" "s", arr);
3. scanf("Name: %10s", arr);
In the last step, the string literals are joined into a single one.
On a side note, your scanf() format string would require the user to literally enter
Name: xyz
to actually match. I doubt this is what you wanted. You probably want something like this:
fputs("Name: ", stdout);
fflush(stdout);
scanf("%" STR(LEN) "s", arr);
Also consider not using scanf() at all. With e.g. fgets(), this whole preprocessor magic is obsolete. For reasons why you shouldn't use scanf(), see my beginners' guide away from scanf().

Related

C macro stringifying a variable

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.
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.
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

Printf format specifier and fields outside quotes

I came across the following code:
#define ERROR 0
#define WARN 1
#define INFO 2
#define DEBUG 3
extern int log_level;
char const *LEVEL_TO_STRING[] = { "ERROR", "WARN", "INFO", "DEBUG" };
#define LOG(level, s, ...) \
do \
{ \
if(level <= log_level) \
printf( "[%s] " s "\n", LEVEL_TO_STRING[level], ##__VA_ARGS__) \
} \
while(0) \
I do not understand what the s is doing outside the quotes in the printf statement. I tried searching for what this is and how it works, but I'm not sure what to look for. Could someone explain to me how this code works?
As a follow-up, is it possible to write code like the example above outside a macro? The closest I've seen to this is using format specifiers:
#define FORMAT "ld"
long num = 1000000;
printf("%" FORMAT "\n", num);
It would help to understand how these two cases work internally, and why C does not let me do something like, printf("%s" s "\n", string1, string2) as is done in the macro above.
EDIT : Not a clean dup of How does concatenation of two string literals work? because this post is specific to printf (and format specifiers) as it relates to macros. Also, there is useful information in the responses to this post that isn't available in the other.
I do not understand what the s is doing outside the quotes in the printf statement
In order to see what happens you need to recall that s is replaced with the second parameter of LOG macro in the text of the program. The only way that this could work is when s is a string literal, because C merges them. In other words, there is no difference between
"quick brown fox"
and
"quick" " brown " "fox"
These two forms of writing a string literal are equivalent.
In the same way, passing "ld" to FORMAT in
printf("%" FORMAT "\n", num);
is equivalent to
printf("%ld\n", num);
and is legal.
why C does not let me do something like, printf("%s" s "\n", string1, string2) as is done in the macro above?
Passing anything other than a string literal is illegal:
char FORMAT[] = "ld";
printf("%" FORMAT "\n", num); // <<== Does not compile
s and FORMAT in your code must be not just strings, but string literals:
#define s "[%s]"
...
printf("%s" s "\n", string1, string2); // This compiles
"[%s] " s "\n"
when s is defined as a macro ie using #define would concatenate everything together.
As the substitution happens during the preprocessing, it won't be flagged as an error. In all other cases, you should get a syntax error.
The key is the line continuation '\' at the end of the definition. The code defines a macro function LOG which does the specified logging.
Apparently the user of the macro can specify their own formatted string in s and give the arguments in ... -> ##__VA_ARGS_

Having troubles formatting scanf while keeping my code understandable

I need a function to read a file name, with a max length of MAX_FILE_NAME_SIZE, which is a symbolic constant, I did this the following way:
char * readFileName()
{
char format[6];
char * fileName = malloc(MAX_FILE_NAME_SIZE * sizeof(fileName[0]));
if(fileName== NULL)
return NULL;
sprintf(format, "%%%ds", MAX_FILE_NAME_SIZE-1);
scanf(format, fileName);
fileName= realloc(fileName, strlen(fileName)*sizeof(fileName[0]));
return fileName;
}
I'd really like to get read of the sprintf part (and also the format vector), what's the cleanest and most efficient way to do this?
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
The C Preprocessor

scanf: template with macro (#define constant) inside

I have a bit of code like here:
#define MAXSIZE 100
int main() {
char str[MAXSIZE+1];
scanf("%100s", str);
...
The problem is I still have "magic number" 100, although defined MAXSIZE.
Is there a way to properly "insert" MAXSIZE into scanf format string? (pure C, c-99 standart)
There is, but you'd be better off using fgets():
if (fgets(str, sizeof str, stdin) != NULL) {
// process input
}
The good thing about this (apart from an undoubted readability boost) is that fgets() takes care of the size correctly, i. e. it accounts for the terminating 0 character (which scanf() doesn't), so you don't have to hack around with adding one to the size when declaring your buffer. It also always NUL-terminates the array for you. Way less error prone.
As to the original question: try the usual "stringify" trick:
#define REAL_STRINGIFY(x) #x
#define STRINGIFY(x) REAL_STRINGIFY(x)
scanf("%" STRINGIFY(MAXSIZE) "s", str);
But this is very ugly, isn't it?

How to pass variable length width specifier in sscanf?

sscanf(input_str, "%5s", buf); //reads at max 5 characters from input_str to buf
But I need to use something like %MACRO_SIZEs instead of %5s
A trivial solution is to create a format string for the same
char fmt_str[100] = "";
snprintf(fmt_str, 100, "%%%ds", MACRO_SIZE);
sscanf(input_str, fmt_str, buf);
Is there a better way to achieve the same?
Like Stefan said, but for sscanf() to more properly answer the question, and with a bit more macro trickery:
#define MACRO_SIZE 5
#define FORMAT(S) "%" #S "s"
#define RESOLVE(S) FORMAT(S)
char buf[MACRO_SIZE + 1];
sscanf(input_str, RESOLVE(MACRO_SIZE), buf);
This uses C's automatic joining together of adjacent string literals, to form the required formatting string at compile-time. This only works if MACRO_SIZE is a preprocessor macro, not if it's a normal runtime variable.
The extra macro call through RESOLVE() is needed since otherwise the argument would not be resolved to its #defined value, and we'd end up with a formatting string of "%MACRO_SIZEs", which is not what we want.
if your MACRO_SIZE is const at compile time, you can try this:
#define MACRO_SIZE "5"
snprintf(fmt_str, 100, "%" MACRO_SIZE "s", buf);
The "correct" solution is what you call the trivial one. All these clever macros(I'd use m4 myself) are just going to make your code less manageable then if you just left it as a constant.
The problem you have here is strings are not a first class data structure in C. They are an array of bytes. Therefor you have to build the array you want to get the meaning you want, and you build that array with sprintf. It's not pretty, but it's correct.
If you're having performance issues and you've tracked it down to here then yes, eliminate the function calls. But unless the value for MACRO_SIZE is repeated a hundred times or spread out over multiple files I'd just change the literal. A macro is just faking having more flexibility, using sprintf actually gives you flexibility.

Resources