19

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?

iBug
  • 35,554
  • 7
  • 89
  • 134
eDeviser
  • 1,605
  • 2
  • 17
  • 44
  • 2
    You don't use a define inside a format string. You use sprintf() to build the format string and pass that to scanf. – jwdonahue Nov 17 '17 at 08:33
  • 2
    @jwdonahue Bad suggestion. – iBug Nov 17 '17 at 08:40
  • @iBug, and you have a better suggestion? – jwdonahue Nov 17 '17 at 08:45
  • 1
    @jwdonahue Not a bad suggestion **in general**, but if you **can** use the preprocessor, it's always better (no runtime overhead). –  Nov 17 '17 at 08:47
  • @everybody, yes of course, I seem to have fallen asleep at the wheel. Good night! – jwdonahue Nov 17 '17 at 08:51
  • @jwdonahue Stack Exchange don't have an `@everyone` feature, though. Just don't @ anyone and everyone will be automatically notified of the new comment. – iBug Nov 17 '17 at 09:20
  • 2
    @iBug this discussion is pointless. You plagiarized **everything**, in many steps following my answer, which mods can see. You're obviously just hunting for reps and badges. This is not a game and plagiarism won't be tolerated. –  Nov 17 '17 at 09:23
  • @FelixPalmen Hello, I am sorry to see someone in distress. I took a look at the comment history and did not really see any evidence of plagarism, rather that two people simply came to the same solution. However, if you still feel that this is the case, please do not be afraid to use the (Flag) link on the appropriate answer. – rlb.usa Dec 06 '17 at 23:10

1 Answers1

32

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

  • Yes, that's it. I remember that I have already seen something like `#define STR_(X) #X` and `#define STR(X) STR_(X)` in some standard header. Do you know where this is defined? – eDeviser Nov 17 '17 at 08:34
  • 1
    @eDeviser these macros *may* appear in a standard header, but I doubt it. They aren't specified by the C standard, so define them yourself. –  Nov 17 '17 at 08:35
  • 1
    @FelixPalmen.: By the way..the `scanf` is containing `Name is`..etc..A rare use I would say. – user2736738 Nov 17 '17 at 09:23
  • @coderredoc yes, it's unlikely that this is indeed what OP wanted. I only focused on the actual question. But good hint, thanks –  Nov 17 '17 at 09:24
  • 1
    @eDeviser I added some info you might find useful. –  Nov 17 '17 at 09:31
  • @Felix You are right. My example is not the best for reading a users input. But my example is enough to understand my question. – eDeviser Nov 17 '17 at 09:40
  • 2
    Definitely the right answer, if someone insists on using `scanf`. It would be wise to add a clear comment just before `#define LEN` mentioning that it _must_ be a simple decimal integer and not an expression etc. – pipe Nov 17 '17 at 12:14
  • @FelixPalmen I have read your guide. It is so cool. Congratulations. – JuMoGar Mar 31 '18 at 19:18
  • 1
    Why is it better to use this solution, rather than just putting #X within scanf? – szpanczyk Feb 21 '19 at 11:42