A few questions related to what you did wrong and how to fix it, but you also said (emphasizing mine):
can someone explain why and why this coding style is bad
I think scanf is a terrible way to read input. This is incompatible with printf , allows you to forget to check for errors, makes it difficult to recover due to errors, and is incompatible with ordinary (and more convenient to perform) read operations (for example, fgets and a company).
First, note that the format "%s" will only be read until it sees spaces. Why spaces? Why does "%s" print an entire line, but read lines in such limited capacity?
If you want to read the entire line, as you can often do, scanf provides ... using "%[^\n]" . What kind? What is it? When did it become Perl?
But the real problem is that none of them are safe. They both overflow freely without checking boundaries. Want to check the boundaries? Well, you get the idea: "%10s" (and "%10[^\n]" starting to look even worse). This will read only 9 characters and automatically add a terminating null character. So good ... because the size of our array will never change.
What if we want to pass the size of our array as an argument to scanf ? printf can do this:
char string[] = "Hello, world!"; printf("%.*s\n", sizeof string, string);
Want to do the same with scanf ? Here's how:
static char tmp[];
This right - scanf does not support the variable "%.*s" printf , therefore, to perform dynamic border checking with scanf we need to build our own format string in a temporary buffer. These are all kinds of bad things, and even though he is really safe here, he will look like a really bad idea for anyone who just stops by.
Meanwhile, let's look at another world. Let's look at the world of fgets . Here, as we read in the data line using fgets :
fgets(buffer, bufsize, stdin);
An infinitely smaller headache, not wasted CPU time, converting integer precision to a string that will be rewritten by the library back to an integer, and all the corresponding elements sit there on one line so we can see how they work together.
Of course, this may not read the whole line. It will read only the whole line if the line is shorter than bufsize - 1 characters. Here we can read the whole line:
char *readline(FILE *file) { size_t size = 80; // start off small size_t curr = 0; char *buffer = malloc(size); while(fgets(buffer + curr, size - curr, file)) { if(strchr(buffer + curr, '\n')) return buffer; // success curr = size - 1; size *= 2; char *tmp = realloc(buffer, size); if(tmp == NULL) /* handle error */; buffer = tmp; } /* handle error */; }
The curr variable is an optimization that does not allow us to double-check the data that we have already read, and is not necessary (although it is useful when we read more data). We could even use the strchr return value to separate the trailing "\n" character if you prefer.
Note also that size_t size = 80; as a starting place is absolutely arbitrary. We could use 81, or 79 or 100, or add it as an argument provided by the user for this function. We could even add the argument int (*inc)(int) and change the size *= 2; on size = inc(size); , allowing the user to control how fast the array grows. They can be useful for efficiency when redistribution becomes expensive and a boat of data rows needs to be read and processed.
We could write the same with scanf , but think how many times we would have to rewrite the format string. We could limit it to a constant increment, rather than doubling (easily) implemented above, and should never adjust the format string; we could give and just save the number, do the math with the above and use snprintf to convert it to a format string with each redistribution, so that scanf can convert it back to the same number; we could limit our growth and initial position so that we could manually adjust the format line (say, just increase the numbers), but after a while it can become hairy and may require recursion (!) for clean work.
It is also difficult to combine reading with scanf with reading with other functions. What for? Suppose you want to read an integer from a string, and then read a string from the next line. You will try this:
int i; char buf[BUSIZE]; scanf("%i", &i); fgets(buf, BUFSIZE, stdin);
This will read "2", but then fgets will read the empty line because scanf did not read the new line! Ok, take two:
... scanf("%i\n", &i); ...
You think that it absorbs a new line, and it does, but it also absorbs leading spaces on the next line, because scanf cannot distinguish newlines from other forms of spaces. (In addition, it turns out that you are writing a Python parser, and line-by-line skipping is important.) To do this, you need to call getchar or read something on a new line and throw it away:
... scanf("%i", &i); getchar(); ...
Isn't that stupid? What happens if you use scanf in a function but do not call getchar because you don’t know if the next read will be scanf or something more sensible (or will the next character be a new line)? Suddenly, the best way to handle the situation is to choose one or the other: do we use scanf exclusively and never have access to the input fgets style full-control, or do we use fgets exclusively and make it harder to perform complex parsing?
Actually, there is no answer. We use only the fgets functions (or not scanf ), and when we need scanf like functionality, we just call sscanf on the lines! We do not need scanf load unnecessarily into our threads! We can fully control our input, which we want, and still get all the functionality of scanf formatting. And even if we couldn’t, many scanf format options have functions that are close to direct matching in the standard library, such as the infinitely more flexible functions strtol and strtod (and friends). In addition, i = strtoumax(str, NULL) for integer types of size C99 looks much cleaner than scanf("%" SCNuMAX, &i); , and much safer (we can use this strtoumax line unchanged for smaller types and allow the implicit conversion to handle extra bits, but with scanf we have to make a temporary uintmax_t to read).
The moral of this story: avoid scanf . If you need the formatting that it provides and don’t want (or cannot) do it (more efficiently) yourself, use fgets / sscanf .