-2

What is the difference between scanf("%s"), scanf(”%[^\n]s") and gets(a) in C programming?

scanf("%[^\n]s", a)

scanf("%s", a)

gets(a)

What is the main difference between the three ways of taking character array input?

akshayk07
  • 2,092
  • 1
  • 20
  • 32
Prometheus
  • 35
  • 10
  • 1
    `puts()` is for output, at least. What is `a`? If it's an array of char or a pointer to first element of such, then `&a` is wrong as it is either a pointer to array of char, or a pointer to pointer to char. – Ilja Everilä Aug 25 '17 at 07:31
  • 2
    Are you sure you mean `"%s[^\n]"`, and not `"%[^\n]"`? Perhaps you need a [good `scanf` (and family) reference](http://en.cppreference.com/w/c/io/fscanf)? – Some programmer dude Aug 25 '17 at 07:31
  • 4
    `scanf("%s[^\n]",a)` is just **wrong**. There's a conversion specifier `s` and a **different** conversion specifier `[]`. They are not meant to be combined. –  Aug 25 '17 at 07:31
  • 1
    @FelixPalmen It's not even a conversion specifier, it's a literal match against that sequence (missing `%` in front). – Some programmer dude Aug 25 '17 at 07:35
  • @Someprogrammerdude yes, the way it's written here, it isn't one. But OP obviously **meant** the conversion specifier `[]`. With "there is", I mean in the specification of `scanf()`. –  Aug 25 '17 at 07:36
  • Writing the `s` **after** `[]` is **still** wrong, but will probably go undetected. They are **different conversion specifiers**, don't combine them. –  Aug 25 '17 at 07:43
  • 1
    Oh, and please don't change code in a question when there are already answers refering to this code. –  Aug 25 '17 at 07:45
  • Note: Looks like a smart-quote in `scanf(”%[^\n]s")`. Better to not use word processing programs when writing code which should be (and w/o the `s`) `scanf("%[^\n]")` – chux - Reinstate Monica Aug 25 '17 at 16:28

2 Answers2

4

scanf("%s",a); will skip over leading whitespace characters in the input, and will match characters until a whitespace character is encountered (or until end-of-file is reached), storing them in the array indicated by the argument. Note that this is susceptible to buffer overflow, so it is best to include a width specifer that provides a maximum number of characters to read from the input. For example, if a is an array of 100 chars, %99s should be used; this leaves space for the \0 terminator that is automatically added by scanf().

Neither scanf("%s[^\n]",a); nor scanf("%[^\n]s", a); is probably what was meant, and instead this should be: scanf("%[^\n]",a);. The %[] is a scanset directive. There is no need to follow the %[] directive with an s, which would only tell scanf() to match a literal s in the input after finishing with %[]. The scanset directive matches characters described within the brackets and assigns them to the corresponding argument. When a match fails, that character is placed back in the input stream. Here, the ^\n indicates that all characters except for newline characters should be matched, so this directive will match characters until a newline is encountered, and the newline will remain in the input stream. The same advice about specifying a maximum width applies here as well: %99[^\n] to avoid buffer overflow, if a is an array of 100 chars. Note that the %[^\n] directive will match any character that is not a \n, including other whitespace characters. This means that it will not skip over leading whitespace characters (but a leading \n in the input will cause the directive to immediately fail, without making an assignment), in contrast to %s, and will read lines of input containing spaces.

puts(a); does not read input, but is an output function. Note that this function prints a newline after printing its argument. Perhaps you meant to include gets() in this list of methods to gather input.

gets(a); is an unsafe function that was deprecated in C99, and completely removed from the language in C11. You should never use this function for any reason. This function fetches a line of input, reading characters until a newline character is encountered, or until end-of-file is reached. The newline character is discarded; it is not stored in the array indicated by a, and it is not returned to the input stream. For this reason, when gets() was used in the past, it worked well with puts(), which automatically prints a newline character after printing its argument.

Finally, for a little more information, there is fgets(). This function fetches a line of input, but takes a size argument so that buffer overflow may be avoided. Given my earlier example of char a[100];, fgets() would be called like this:

fgets(a, 100, stdin);

or sometimes:

fgets(a, sizeof a, stdin);

Here, fgets() reads at most one character less than the number specified by the size argument, allowing space for a \0, which is always added. If the \n is read, it is stored in a. Since the newline is not discarded, puts() does not work as well here; often the newline needs to be removed after taking input with fgets().

ad absurdum
  • 19,498
  • 5
  • 37
  • 60
  • Sorry, my last edit happened during your elaborative answer. OP obviously didn't think a second before posting this, please feel free to edit it back, I'll abandon this now... –  Aug 25 '17 at 07:49
  • Do you mean the `gets()` that is now in the answer? I just saw that and was updating.... – ad absurdum Aug 25 '17 at 07:50
  • 1
    Ok then ... at least the code in the question now is what OP meant. I wished people would take a second to proof-read what they're asking before hitting the post button :o –  Aug 25 '17 at 07:51
  • A part missing is the result of `scanf("%[^\n]",a);` when the first character is `'\n'`. `scanf("%[^\n]",a);` fails to save anything in `a` in that case. – chux - Reinstate Monica Aug 25 '17 at 16:27
  • @chux-- I thought that I covered this case with "(but a leading `\n` in the input will cause the directive to immediately fail, without making an assignment)".... – ad absurdum Aug 25 '17 at 22:54
2

First of all, they all have undefined behavior for the same reason: They will read a number of characters you can't know in advance, but through your pointer, you provide storage where to store this data, and this storage has some fixed size. Therefore, there are always inputs possible that will overflow your buffer. You should'nt ever use any of these lines in your code.

That said:

  • gets() reads a line of input and stores it to your buffer. It also reads the newline character, but this isn't stored. This function is broken by design, as there is no way to use it safely. Don't ever use it. It has been removed from the C standard in C11, so you could only use it with older standards anyways. The correct replacement is char a[100]; fgets(a, sizeof a, stdin);. This will store the newline character in the buffer, though.

  • scanf("%[^\n]s", ...) will also read a line of input. The [] conversion specifier takes a list of accepted or, with ^ in front, rejected characters. So with this syntax, it will accept any character except newline. Therefore, the newline character won't be read this way. The s would be searched in the input literally, it's not what you mean. s and [] are different conversion specifiers. To use this one correctly, you must use the field with like so: char a[100]; scanf("%99[^\n]", a);. Note that you have to specify one less than your buffer size because scanf() doesn't count the '\0' character that's appended as an end mark for the string.

  • scanf("%s", ...) reads a "word", that is it stops at the first whitespace character. To use this correctly, as above, use something like char a[100]; scanf("%99s", a);.

  • Love the phrase "broken by design".... I know what you mean, but it almost sounds like the designers _meant_ for `gets()` to be broken ;) – ad absurdum Aug 25 '17 at 08:09
  • Haha, never thought about *this* with such a phrase. Of course I just mean the design is wrong to an extent making it impossible to "fix" in the implementation. –  Aug 25 '17 at 08:17
  • "scanf("%s", ...) reads a "word", that is it stops at the first whitespace character." omits the parts that leading white-space are read and discarded, then reading a "word" commences. – chux - Reinstate Monica Aug 25 '17 at 16:34