3

I have this code below and I'm curious what would be stored in the memory if I entered

"Hi" then I'll press enter, will 'H' 'i' '\n' '\0' be stored in the memory

and if I enter

"Hello" then I'll press enter, will 'H' 'e' 'l' 'l' '\0' be stored in the memory then 'o' '\n' be in the buffer

and lastly, if I enter

"Hell" then I'll press enter, will 'H' 'e' 'l' 'l' '\0' be stored in the memory then '\n' be in the buffer

char str [5];

fgets(str, 5, stdin);
printf("%s", str);
Janjan
  • 85
  • 7

3 Answers3

3

fgets(buf, n, stream) reads input and saves it into buf until 1 of 4 things happen.

  • Buffer is nearly full. Once n-1 characters read (and saved), '\0' is appended to buf. Function returns buf. There is likely remaining characters in stream to read.1

  • '\n' is read from stream. '\n' is appended to buf. '\0' is appended to buf. Function returns buf. The line has been completely read.

  • End-of-file occurs. Had some characters been read before, '\0' is appended to buf. Function returns buf. Else NULL is returned.

  • Input error (rare). NULL is returned. state of buf is indeterminate.

The only thing different about reading '\n' versus other characters is that it informs fgets() to stop reading.


1 Should a full buffer get read without '\n', to read and toss the rest of the line:

int ch;
while ((ch = fgetc(stream)) != '\n' && c != EOF) {
  ;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • What if in the case of exactly n-1 character then I clicked enter on my keyboard? Will the newline char remain in the buffer or stored in the memory as 'a'....'n-1' '\n' and the newline char replaced with a null byte or will it store 'a'....'n-1' '\0' and leave the newline char in the buffer – Janjan Mar 03 '20 at 15:54
  • 1
    @Janjan "newline char replaced with a null byte" is incorrect. See "The only thing different about reading '\n'...." above. The Enter key generates `'\n'` - that is a character too. Is `'\n'` one of the `n-1 characters in your question? – chux - Reinstate Monica Mar 03 '20 at 15:58
  • I mean no disrespect but is the answer provided by anastaciu in the comments incorrect? "Yes, as you input "Hell" followed by enter the buffer will have "H e l l \n" so when it's stored, '\n' will be replaced by '\0'." – Janjan Mar 03 '20 at 16:00
  • Yep, I immediately realized my mistake haha – Janjan Mar 03 '20 at 16:01
  • Does this mean that "Hell" then I'll press enter, will 'H' 'e' 'l' 'l' '\0' be stored in the memory then '\n' be in the buffer is correct. While 'H' 'e' 'l' 'l' '\n' stored in the memory however \n will be replaced with \0 making it H e l l \0 is incorrect – Janjan Mar 03 '20 at 16:03
  • 2
    @Janjan That comment is amiss. With a short buffer it is _as if_ \n was replaced with \0, with \n still in the stream, yet a better way to look at it is that characters pass n-1 are not read (\n or otherwise), a \0 is appended and fgets() returns like the first bullet of this answer. – chux - Reinstate Monica Mar 03 '20 at 16:03
  • I have tried using two fgets both reading an n of 5, while I entered Hell and clicked enter both of it are executed. Does it mean that my original understanding that \n is left in the buffer is correct? Did I undersrand correctly this time? – Janjan Mar 03 '20 at 16:05
  • 1
    Appears correct. The first `fgets(buf, 5, stdin)` read the first 4 of the 5 characters, H,e,l,l,\n. \n remains in `stdin` The 2nd `fgets(buf, 5, stdin)` reads the \n and stops. – chux - Reinstate Monica Mar 03 '20 at 16:07
  • Thank you very much, so my original assumption was indeed correct. Kindly please answer this with jusg a yes or no, I just want an assurance haha. – Janjan Mar 03 '20 at 16:08
  • @Janjan Too much unclear about what your original assumption were and your understanding now. Let us focus on code. – chux - Reinstate Monica Mar 03 '20 at 16:13
  • This one: if I input "Hell" then press enter, 'H' 'e' 'l' 'l' '\0' be stored in the memory then '\n' be in the buffer. – Janjan Mar 03 '20 at 16:14
  • With `n==5`, yes. [Ref](https://stackoverflow.com/questions/60509461/behavior-of-fgets-newline-character-and-how-it-gets-stored-in-the-memory/60510945?noredirect=1#comment107049610_60510945) – chux - Reinstate Monica Mar 03 '20 at 16:15
  • Thank you! Now I fully understand what fgets does. Thank you!!! – Janjan Mar 03 '20 at 16:16
  • 1
    @chux-ReinstateMonica, I've read this interesting exchange and I would like to ask you, do you mean that if the size of the inputed string is equal to the size of the container, \n included, \n will reamain in the buffer, what about if I input "Hel" for this specific case? Can you point me to any docs that standardize this behaviour? – anastaciu Mar 03 '20 at 16:23
  • 2
    @anastaciu `'\n'` remains in `stdin` in first case. Perhaps "The fgets function reads at most one less than the number of characters specified by n from the stream pointed to by stream into the array pointed to by s. No additional characters are read after a new-line character (which is retained) or after end-of-file. A null character is written immediately after the last character read into the array." C17 § 7.21.7.2 2 – chux - Reinstate Monica Mar 03 '20 at 16:35
  • 1
    @chux-ReinstateMonica, thanks for your reply, that clarifies things. – anastaciu Mar 03 '20 at 16:45
2

Given the definition of fgets():

char *fgets( char *str, int count, FILE *stream ); (until C99)

char *fgets( char *restrict str, int count, FILE *restrict stream ); (since C99)

Reads at most count - 1 characters from the given file stream and stores them in the character array pointed to by str. Parsing stops if a newline character is found, in which case str will contain that newline character, or if end-of-file occurs. If bytes are read and no errors occur, writes a null character at the position immediately after the last character written to str.

The behavior is undefined if count is less than 1. It is also not specified whether a null character is written if count==1.

fgets stores the inputed line with the maximum length (size) of 5 in the str variable and null terminates it.

Since the size you provide is 5, if you input "Hello" it will store H e l l \0 replacing the 'o', 'o' is not stored and, normally, 'o' and '\n' will remain in the stdin buffer though this is not standard mandated.

If you intput "Hell" the stdin buffer will have H e l l \n so when its stored '\n' will be replaced by '\0', and '\n' will remain in the buffer.

In the same way if the line is smaller than 5 - 1, nothing is replaced, the char array is null terminated i.e. "Hel" will be stored as H e l \n \0, and the stdin buffer will be emptied.

This is why you normally declare your char array 1 char bigger than the actual max size expected, and pass its size to fgets:

fgets(str, sizeof(str), stdin);

Note that you should not use fflush(stdin).

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • "If you intput "Hell" then '\n' will be replaced by '\0'" , will this newline character be in the buffer waiting to be read? – Janjan Mar 03 '20 at 14:51
  • 1
    Yes, as you input "Hell" followed by enter the buffer will have "H e l l \n" so when it's stored, '\n' will be replaced by '\0'. – anastaciu Mar 03 '20 at 14:54
  • So instead of storing \n, \0 will be stored then \n will be left in the buffer, am I right? – Janjan Mar 03 '20 at 14:58
  • 1
    @Janjan, it will not, this will only happen if the string is bigger than 5, if you input "Hello", 'o' is replaced and '\n' will remain in the buffer. – anastaciu Mar 03 '20 at 15:00
  • May I ask why are there conflicting answers on this thread :(. Please see the answer of Samuel Peter. Sorry, i"m just really confused. – Janjan Mar 03 '20 at 15:05
  • 1
    As far as I can tell Samuel Peter is correct the standard does not mandate it, but this is usually what happens. – anastaciu Mar 03 '20 at 15:08
  • So my interpretations as posted above are correct except when reading Hell? It should be "Hello" then I'll press enter, 'H' 'e' 'l' 'l' '\n' be stored in the memory however \n will be replaced with \0 making it H e l l \0 ? – Janjan Mar 03 '20 at 15:10
  • 1
    @Janjan Yes, that's it. – anastaciu Mar 03 '20 at 15:12
  • 1
    Thank you very much, this helps up a lot :) – Janjan Mar 03 '20 at 15:12
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/208932/discussion-between-janjan-and-anastaciu). – Janjan Mar 03 '20 at 15:16
  • May I request a chat with you, I may have some further clarifications – Janjan Mar 03 '20 at 15:18
  • Detail: "fgets stores the inputed string" is not bad, yet conflagates _string_. As C Lib has a specific meaning for _string_, that does not match what the user types (no \0). `fgets()` is more like taking the user inputted _line_ and converting it to a _string_. – chux - Reinstate Monica Mar 03 '20 at 15:29
  • 1
    @chux-ReinstateMonica, Term correction is definately important, I corrected it. – anastaciu Mar 03 '20 at 15:38
1

From man fgets on my machine:

char * fgets(char * restrict str, int size, FILE * restrict stream);

The fgets() function reads at most one less than the number of characters specified by size from the given stream and stores them in the string str. Reading stops when a newline character is found, at end-of-file or error. The newline, if any, is retained. If any characters are read and there is no error, a `\0' character is appended to end the string.

This means that you are correct about what will be stored in your str buffer. The unread characters can presumably be read by a subsequent read on stdin, but this will depend on the OS and where you are reading from. The C standard doesn't mandate it.

Samuel Peter
  • 4,136
  • 2
  • 34
  • 42
  • How about the newline characters and the way it is stored in memory? Is my interpretation of it correct? – Janjan Mar 03 '20 at 14:53