0

I'm using EOF to jump out of a 'while' loop, and want to input some numbers by 'scanf'.The 'scanf' outside the loop doesn't work on macos.

I've tried running this code on macos and centos.The result on centos is what I need.

#include <stdio.h>
#include <stdlib.h>
int main(){
 int i;
 //ctrl+d=EOF
 while(scanf("%d",&i) != EOF){
  printf("?");
 }
 printf("\nloopend\n");
 //those 'scanf' are ignored on macos.
 scanf("%d",&i);
 scanf(" %d",&i);
 scanf("%d ",&i);
 printf("\nend\n");
}

Input(without ','): 1,\n,ctrl+d

Output(centos):

1
?
loopend
//waiting for input here

Output(macos):

1
?
loopend

end
//the program ended directly
M.M
  • 138,810
  • 21
  • 208
  • 365
ZHUZHU-3
  • 45
  • 5

1 Answers1

4

The MacOS behaviour is correct. According to the C standard §7.21.7.1/3 (the fgetc library function), the end-of-file indication is sticky; once fgetc sees an EOF, it must set the file's end-of-file indicator which will cause subsequent calls to return EOF until the end-of-file indicator is cleared, for example with clearerr():

If the end-of-file indicator for the stream is set, or if the stream is at end-of-file, the end- of-file indicator for the stream is set and the fgetc function returns EOF. Otherwise, the fgetc function returns the next character from the input stream pointed to by stream. If a read error occurs, the error indicator for the stream is set and the fgetc function returns EOF.

Since other input functions, including scanf, are supposed to act as if implemented by repeated calls to fgetc, EOF should be sticky for them, too. If you want to continue reading after you receive an EOF return, you should call clearerr() on the stream. (Or something else which resets the indicator, such as seek().)

For many years, the Gnu implementation of the standard C library did not follow the standard. It only reported EOF once, leaving the next fgetc to wait for more input on devices like terminals and pipes. The bug, reported in 2006, was finally fixed in v2.28, released in August 2018, although that might not yet be part of the Centos distro.

[Note: there is a longer discussion about this behaviour in this answer, including a now-outdated grump (by me) and some links to historic discussions about the issue.]

In any case, it has always been clear that portable code should call clearerr(), since BSD-derived standard library implementations (including MacOS) follow the standard as quoted above.

rici
  • 234,347
  • 28
  • 237
  • 341
  • I wholeheartedly agree that the GNU library is aberrant — it shouldn't matter that it's a terminal input (which is required to get that behaviour). Once EOF is seen, it should remain 'seen' until and unless a `clearerr()` call is made. – Jonathan Leffler Jul 07 '19 at 04:44
  • @jonathan. Yeah, I've been complaining about this for years. But apparently glibc was finally fixed, so I edited the answer. – rici Jul 07 '19 at 04:47
  • 1
    If it has been fixed at last, then "was aberrant" is more appropriate than "is aberrant". Version 2.28 was released in August 2018; current version is 2.29 (2019-01-31). New versions are released every 6 months. Not quite a year since the fix release — it will only be available in leading edge systems (not the stuff I work with, regrettably). (See https://www.sourceware.org/ml/libc-alpha/2018-08/msg00003.html in the section "Deprecated and removed features, and other changes affecting compatibility".) – Jonathan Leffler Jul 07 '19 at 04:55
  • @jonathan: exactly the message I found when searching for the thread in which glibc maintainers obstinately insisted that the behaviour wouldn't be changed. Nice surprise, although it's no longer really relevant to anything I do. – rici Jul 07 '19 at 05:01
  • I wonder if they can get `pwrite()` fixed so that it isn't broken (according to the POSIX spec) by `O_APPEND` mode? Probably not; that's more likely to be a bug in the kernel than the GNU C Library. – Jonathan Leffler Jul 07 '19 at 05:04
  • 1
    @jonathanleffler yup documented kernel feature, as I read so on the manages just this week... – Antti Haapala -- Слава Україні Jul 07 '19 at 12:26
  • I'm glad to read your paragraph starting "For many years," , that behaviour always puzzled me by contradicting how I thought streams were supposed to work (i.e. if it's closed it's closed) – M.M Jul 08 '19 at 06:40
  • "once fgetc sees an EOF, it must set the file's end-of-file indicator which will cause subsequent calls to return EOF " --> as I read the spec, an `EOF` returned due to a rare input error is not sticky - even thought the end-of-file flag is sticky. An EOF due to input error may or may not be followed by a non-`EOF` result. – chux - Reinstate Monica Jul 08 '19 at 21:40
  • @chux: you're probably right. But that's not an EOF; it's an fgetc call which returns EOF. I didn't say that EOF was sticky once fgetc returns an EOF; I said it was sticky once it *sees* an EOF. If there's an error, the error flag is set but apparently it's not required that `fgetc` test the error flag. – rici Jul 08 '19 at 21:49