8

This is a very basic C question, coming from page 18 of Kernighan and Ritchie.

I've compiled this very simple code for counting characters input from the keyboard:

#include <stdio.h>

/* count characters in input; 1st version */
main()
{
  long nc;

  nc = 0;
  while (getchar() != EOF)
    ++nc;
  printf("%1d\n", nc);
}

This compiles fine, runs fine, and behaves pretty much as expected i.e. if I enter "Hello World", it returns a value of 11 when I press CTRLD to give the EOF character.

What is confusing me is if I make a mistake, I can use backspace to delete the characters and re-enter them, and it returns only the number of characters displayed by the terminal when I invoke EOF.

If the code is counting each character, including special characters, if I type four characters, delete two, and type another two, shouldn't that output as 8 characters (4 char + 2 del + 2 char), not 4?

I'm obviously misunderstanding how C handles backspace, and how/when the code is incrementing the variable nc?

Spikatrix
  • 20,225
  • 7
  • 37
  • 83
Philip King
  • 91
  • 1
  • 4
  • 4
    The editing is handled by the terminal application, so the `getchar` never reads the deletions. – Some programmer dude Feb 14 '17 at 10:27
  • Note: You should use `int main(void)` instead of `main()` as the former is the valid one according to the standard. – Spikatrix Feb 14 '17 at 11:58
  • @CoolGuy: Actually, the second version is still valid, but a legacy. It is an obsolescence feature and might be removed from the standard. Said that: yes, the first version should definitively be used. In general prototype-style signatures should be used for functions in general. – too honest for this site Feb 14 '17 at 12:10
  • @Olaf No, `main()` without an explicit `return` has never been a valid program. Implicit `int` during function declarations is not just obsolescent, the feature was removed entirely with C99. So the code from K&R is incorrect for any version of the C language. – Lundin Feb 14 '17 at 13:11
  • @Lundin I might err about the omitted `int` (just checked: you are correct), but an explicit `return` is not required for `main` (that is different from other functions. See the standard: http://port70.net/~nsz/c/c11/n1570.html#5.1.2.2.3p1 – too honest for this site Feb 14 '17 at 13:27
  • @Olaf Main can skip return as per C99. It was not allowed to do so in C90. K&R tried to follow C90 with their 2nd edition but often failed. I've written a summary of all valid forms of main() with references, [here](http://stackoverflow.com/a/31263079/584518). – Lundin Feb 14 '17 at 13:31
  • @Lundin Ok, so I missinterpreted your comment. Anyway, I think we agree both, non-prototype signature and omitting `return` is bad style and should be avoided. – too honest for this site Feb 14 '17 at 13:34
  • 1
    @Olaf Yes, which is why K&R should not be read. As it turns out, the programming world's single-most famous example all categories, `main() { printf("hello, world\n"); }` is incorrect... it invokes UB in C90 (no return type) and won't even compile in later versions (implicit int not allowed). – Lundin Feb 14 '17 at 13:42
  • @Lundin: I never used this anyway, that's why I I was wrong here. It never has been good practice and I threw away the first ed of K&R after the first chapters and moved to Modula-2 instead. When I used C later, it was C90. Coming from the Wirth-languages, I never liked the floppyness of C anyway. But that's what we have now, and - judging from the speed of development of the standard - in 20 years. – too honest for this site Feb 14 '17 at 13:53
  • See also [Canonical vs non-canonical terminal input](http://stackoverflow.com/questions/358342/canonical-vs-non-canonical-terminal-input/358381#358381). – Jonathan Leffler Feb 14 '17 at 16:53
  • @Lundin well the code from K&R was correct for K&R C :) – M.M Feb 15 '17 at 06:47
  • Finer grained control over input and the screen can be had with libraries like [curses](http://heather.cs.ucdavis.edu/~matloff/UnixAndC/CLanguage/Curses.pdf). – Schwern Feb 15 '17 at 07:07
  • TBH I wouldn't normally leave out the "void" - I'm just going through K&R (ANSI C 2nd Ed) to try and cement some of the basics, and the above code is a character-for-character copy. Thanks all for the information about the validity (or non-validity) of the approach, even if it was a little off topic. :-P – Philip King Feb 15 '17 at 12:58

3 Answers3

6

Typically, your terminal session is running in "line mode", that is, it only passes data to your program when a line is complete (eg, you pressed Return, etc). So you only see the line as it is complete (with any editing having been done before your program ever sees anything). Typically this is a good thing, so every program doesn't need to deal with delete/etc.

On most systems (eg Unix-based systems, etc), it is possible to put the terminal into "raw" mode -- that is, each character is passed as received to the program. For example, screen-oriented text editors commonly do this.

John Hascall
  • 9,176
  • 6
  • 48
  • 72
6

It's not that getchar() doesn't count the "deletions" but it doesn't even see the input until it's passed to your program by the terminal driver.

When you input something, it doesn't reach your C program until you press \n or send EOF (or EOL). This is what POSIX defines as Canonical Mode Input Processing - which is typically the default mode.

P.P
  • 117,907
  • 20
  • 175
  • 238
2

Backspace characters are normally used to edit input in cooked tty mode (see canonical input mode in tty(4) in BSD and termios(3) in linux systems), so they are consumed at the tty driver, and don't get to the input the process gets after that. The same applies to Ctrl-D as the end of file or to Ctrl-K as the kill input character. There are several things the driver does behind the scenes that your process doesn't get finally. These are directed to make life easier to users and programmers, as you normally don't want erased input in your life (that's the reason of erasing it), or want line endings to be \n and not \r as the tty normally generates when you press the [RETURN] key. But if you read from a file that happens to have backspaces, you'll get them as normal input anyway, just create a file with backspaces and try to read redirecting input from it, and you'll see those characters in your input.

By the way, if you want to generate backspaces at the terminal, just prepend a Ctrl-V character before each (this is also managed at the tty driver and will not happen when reading from a file) and you'll see your backspace chars as normal input in your file (to send a Ctrl-V just double it)

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31