2

I was working on the following example of C code from Deitel & Deitel. It seems that the code is supposed to print the characters entered before EOF, in the reverse order. But I have to press EOF (ctrl+z in windows) several times and Enter key to get it done. Could you please let me know why it does not respond at the first EOF?

 #include <stdio.h>

 int main( void )
 {
     int c;
     if ( ( c = getchar() ) != EOF ) {
         main();
         printf( "%c", c );
     } /* end if */

     return 0; 
 } 
abligh
  • 24,573
  • 4
  • 47
  • 84
Cror2014
  • 417
  • 1
  • 6
  • 16
  • 7
    That code is not a very good example. You should *never* call `main` recursively. – Some programmer dude Dec 18 '16 at 20:11
  • 2
    And what does "scanf" (mentioned in the title) have to do with this question? – Some programmer dude Dec 18 '16 at 20:15
  • `Ctrl-Z` must be the first entry, or the first entry after a `newline`. When using `scanf` the key combo `Ctrl-Z` must be pressed three times, I am still unclear as to why. – Weather Vane Dec 18 '16 at 20:19
  • 4
    @Someprogrammerdude Turns out there's nothing technically wrong with calling `main` recursively. For a tiny example program I guess it's ok. http://stackoverflow.com/questions/4238179/calling-main-in-main-in-c#4238188 – Schwern Dec 18 '16 at 20:20
  • 1
    @Schwern you are correct that unlike in C++ (where calling `main()` is expressly prohibited), calling `main()` in C is not prohibited by the standard. But I wouldn't say that 'there's nothing technically wrong with it'; it's still a bad idea - for instance as `main()` is (from memory) not forced to `return`. – abligh Dec 18 '16 at 20:54
  • I thought this question was simple. It's actually quite subtle. Try it under Linux and the number of `^D` required surprised me. – abligh Dec 18 '16 at 21:05
  • @WeatherVane: Care to post the code you used to test the scanf call? – rici Dec 18 '16 at 21:14
  • The requirements for a keyboard to signal _end of file_ or EOF are OS and maybe keyboard dependent, not`C`. Post your OS and compiler used. – chux - Reinstate Monica Dec 18 '16 at 21:15
  • @rici `#include int main(void) { int i; while(scanf("%d", &i) == 1) { printf("Input %d\n", i); } return 0; }`. This is with MSVC run from console. – Weather Vane Dec 18 '16 at 21:19
  • @WeatherVane: If your input to that function consists of, for example, `42`, then you would need the three EOF (ctrl+d) characters in Linux, too; (I don't have MSVC to test it with), but it's because you use `%d` rather than the expected `%c` to read data. The first ctrl+d terminates the underlying input, which means scanf sees `42` but that's not enough to satisfy it; it needs to see something which terminates the number. The second ctrl+d causes a zero-length line to be read, which is treated as an EOF; now scanf returns 1, which means that it gets called again. to read again. – rici Dec 18 '16 at 21:44
  • @rici it does not matter how many times I type `Ctrl-Z` in each input line, each of the three series needs a `newline`. So `Ctrl-Z Ctrl-Z Ctrl-Z newline` need to be repeated three times. Or `Ctrl-Z newline` repeated three times. Or any permutation. With `char` input instead of `int` it requires twice. In each example there was no `42` (`int`) or `*` (`char`) entered - it wasa directly afdter a previosu `newline`. – Weather Vane Dec 18 '16 at 21:48
  • @weatherVane: That's interesting. Here's my theory: on windows, the ctrl+z is actually passed through to the user program but since the console is opened in text mode, the standard c library treats it as an EOF with the result that the rest of the line (including the newline character and any additional ctrl+z characters) is ignored. The rest is the same as the Linux case; the first EOF only terminates the input line (since there is data on that line); the second one terminates the scanf; and the third one causes the second scanf to return -1... – rici Dec 18 '16 at 22:00
  • ... In theory, an EOF should be "sticky" but console EOFs are not on Linux, and its possible that is also true on Windows. (That "in theory" is my theory based on my reading of the standard but it's a bit controversial.) – rici Dec 18 '16 at 22:01
  • @rici thanks for the observations. Note that `scanf`does not even return until the series is complete - no program output. PS sorry about the previous dyslexic rubbish - ran out of edit time. – Weather Vane Dec 18 '16 at 22:04
  • @abligh "main() is (from memory) not forced to return." - huh? `main` has no special properties regarding returning, other than you're allowed to omit the return statement and the compiler will substitute `return 0;` – M.M Dec 19 '16 at 00:02
  • @Someprogrammerdude, why is it illegal to call main recursively ??? I was always told that `main()` was a function like any other and no difference when compiling. Please, if you do such an asseveration, just justify why. In this case it just solves the problem in an elegant way. – Luis Colorado Dec 19 '16 at 18:57
  • @LuisColorado It's not "illegal" in C, just a very bad habit. Also, using recursion for simple loops (which this is) is also a very bad habit. Recursion generally makes a program harder to read and maintain, as well as making it prone to stack overflows. – Some programmer dude Dec 19 '16 at 19:11
  • You strongly recommended to _never_ do it when `main()` is a function as any other. I have more than 40 years experience writing C code, so I know the drawbacks. Your strong emphasis with a newbie can make him to reach to the wrong conclussion (despite the fact he got a bad example at first). And I'm not going to enter if Date example is good or bad (but I should have never selected such an example in teaching, of course) – Luis Colorado Dec 19 '16 at 19:16

2 Answers2

1

Well getchar(3) is a function that operates in buffer mode, so you have to input some characters, and press the character used to signal end of data (^D in unix, ^Z in windows)

The problem here is that windows console driver is not specified the same way as the unix tty driver, so the behaviour will, in general, not be the same... Try to test the program in a real unix environment (or linux) and see if the input, at least is reversed, as the example said.

In unix, the behaviour of terminal input is that ^D is interpreted as soon as it is pressed, but if some input is in the driver buffer before it, it will make those input available to the program (so you'll have to press it a second time to signal EOF condition, which consists in a read(2) that results in 0 characters actually read). In case you have pressed <Return> before ^D (return makes all data available to the application, with the difference that the \n char is also appended to the data read), the input buffer is empty, so the EOF condition comes inmediately, after the <return> char.

In windows, you need to press <return> for anything to be read (the ^Z to be interpreted), and things complicate.

By the way, I have executed your program on a BSD unix system, with the following result:

$ a.out
apsiodjfpaosijdfa
^D
afdjisoapfjdoispa$ _

Explanation: first line is the input line "apsiodjfpaosijdfa", followed by a \n, and ^D signalling end of input. All this data goes to the application at once, and getchar() then processes it character by character. It prints the \n first (making the line to appear below ^D) and then the input chars, reversed. As there was no \r at the beginning of data, no return is issued at end, and the prompts appears next to the output. The final _ signals the position of the cursor at the end.

If you don't want to deal with end of data characters (or don't have any unix at hand to make the test) you can use a text file to test your program (no eof char, only the actual end of the file), by redirecting program input from a file, like in this example that uses the original source code as input:

$ a.out <pru.c

}
;0 nruter     

/* fi dne */ }     
;) c ,"c%" (ftnirp         
;)(niam         
{ ) FOE =! ) )(rahcteg = c ( ( fi     
;c tni     
{
) diov (niam tni

>h.oidts< edulcni#$ _ 
Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
0

It's to do with they way the windows console is passing the CTRL+Z to the program. It's probably waiting for you to compose a line and not recognising a line until it has a non- CTRL-Z character in it. So it waits until you accidentally press space and also enter.

Just echo everything in a scratch program to see exactly what is going on.

Malcolm McLean
  • 6,258
  • 1
  • 17
  • 18