-1

In a group assignment, we have to read input which only consist of the (small) letters a-z and the characters have a max length of 255. I wanted to check with getchar and ASCII, but my partner found a solution using sprintf and scanf, which I do not totally understand:

#include <stdio.h>

int main() {

int result = 0;

unsigned int length = 255;
char input[257];

result = readInput(input, &length);

return 1;
}

int readInput(char *output, int *length) {

char format_pattern[15]; 
sprintf(format_pattern, "%%%u[^\n]%%n", *length); 

printf("Max allowed length is %d\n",*length);

scanf(format_pattern, output, length);

printf("Input length is %d\n",*length);

return 1;

}

Output: Max allowed length is 255 testinput Input length is 9

How does the format pattern in sprintf and scanf work? Especially the three %%% before u and the two %% before n - I tried changing this to %u[^\n]%n because two ## would escape the ´%´, but then I get an error so they have to be there.

The only things I figured out are:

  • the %n can read the characters before it, e.g.:

    int var;
    
    printf("Some Text before%n and after",&var); 
    printf("characters before percent n = %d\n", var);
    

    Output: Some Text before and aftercharacters before percent n = 16

    but in my big example above there isn't a pointer variable, where the amount of text could be stored since *length is for %%%u?

  • The [^\n] means something like "read till new Line"

I googled a lot but did not find a similiar example - could somebody help me?

s.r.
  • 146
  • 8
  • " the characters have a max length of 255" - what does that mean? `char` has a fixed size of `CHAR_BIT` bits (and that's typically much less than 255). Also which language is this, C or C++? Why is it tagged with two different languages? If your partner came up with this code, he should be able to explain. (unless your partner's name is "google") – too honest for this site Dec 06 '18 at 15:38
  • 1
    To understand the format string, you could simply check what is the result in `format_pattern`. You could simply print it or watch it in a debugger. In most cases seeing the result helps to understand what is going on. – Gerhardh Dec 06 '18 at 15:46
  • 1
    "but my partner found a solution using sprintf and scanf, which I do not totally understand:" --> Why not ask your partner? – chux - Reinstate Monica Dec 06 '18 at 16:48
  • 1
    Possible duplicate of [How to escape the % (percent) sign in C's printf?](https://stackoverflow.com/q/1860159/608639), [Format string with multiple percent signs](https://stackoverflow.com/q/44777298/608639), [What are scanf(“%*s”) and scanf(“%*d”) format identifiers?](https://stackoverflow.com/q/2155518/608639), etc. – jww Dec 07 '18 at 01:52

2 Answers2

3

Assuming x is a (large enough) char array

sprintf(x, "%%"); // put a single % (and '\0') in x
sprintf(x, "%u", 8); // put 8 (and '\0') in x

sprintf(x, "%%%u", 8); // put %8 (and '\0') in x
pmg
  • 106,608
  • 13
  • 126
  • 198
  • so my code `sprintf(format_pattern, "%%%u[^\n]%%n", *length);` means: "put %256 (*length), [^\n] and %n in format_pattern" Right? – s.r. Dec 06 '18 at 15:28
  • 1
    Right, except `*length` is 255 :) – pmg Dec 06 '18 at 15:45
1

Your partner is using sprintf() to dynamically create a format string for scanf(). This is a bit tricky, because the printf and scanf functions use mostly the same formatting language. The reason for the dynamic format creation appears to be to insert a configurable field width into the format. That's clever, but overcomplicated and wholly unnecessary.

How does the format pattern in sprintf and scanf work? Especially the three %%% before u and the two %% before n

%%%u is actually two directives. The first %% causes printf to emit a single % character, and that leaves %u, which I think you recognize. Similarly, the %%n is one directive (%%, as described above) plus a literal 'n' character, which is emitted as-is. The overall sprintf call prepares a format string something like "%255[^\n]%n".

How, then, does the resulting format work with scanf? You should probably read its documentation, available all over, such as here. That one happens to be for the GLIBC implementation, but you're not using anything non-standard, so it should explain everything you need to know about your particular format. Specifically, the 255 that you go to such trouble to introduce is a maximum field width for a field consisting of characters in the "scanset" described by [^\n] -- every character except newline. The %n does not consume any input; instead, it stores the number of characters so far read by that scanf call.

For your purposes, I see absolutely no reason to generate the scanf format dynamically. You are given a maximum field width, so you might as well use it in a literal format string. Even if you wanted a maximum field width specified at runtime, scanf() has a better mechanism than dynamically writing format strings, involving passing the wanted field width as an argument.

Bonus hint: the code you posted is terribly mixed up about where to use *length and where just length in your printf and scanf calls. Where you want to output its value, you must pass the value (length). Where you want scanf to modify the value, you must pass the address where the new value should be stored (*length).

John Bollinger
  • 160,171
  • 8
  • 81
  • 157