0

This is my first question on stackoverflow. Pardon me if I haven't searched properly but I do not seem to find an explanation for this. Was just attempting an example from Bjourne Stroustroup's papers. Added my bits to see the array get re-sized as I type the text.

But it doesn't seem to work that way! getchar() simply waits till I am done with entering all the characters and then it will execute the loop. As per the logic, it doesn't actually go into the loop, get a character, perform its actions and then iterate. I am wondering if this is implementation specific, or intended to be like this?

I am on Ubuntu 14.04 LTS using Codeblocks with gcc 4.8.2. The source was in cpp files if that matters.

while(true)
{
    int c = getchar();
    if(c=='\n' || c==EOF)
    {
        text[i] = 0;
        break;
    }
    text[i] = c;

    if(i == maxsize-1)
    {
        maxsize = maxsize+maxsize;
        text = (char*)realloc(text,maxsize);
        if(text == 0) exit(1);
        cout << "\n Increasing array size to " << maxsize << endl;
    }

    i++;
}

The output is as follows:

Array Size is now: 10 Please enter some text: this is some sample text. I would have liked to see the memory being realloced right here, but apparently that's not how it works!

Increasing array size to 20

Increasing array size to 40

Increasing array size to 80

Increasing array size to 160

You have entered: this is some sample text. I would have liked to see the memory being realloced right here, but apparently that's not how it works!

Array Size is now: 160

2 Answers2

2

This has nothing to do with getchar directly. The "problem" is the underlying terminal, which will buffer your Input. The Input is sent to the program after you press enter. In Linux (dunno if there is a way in Windows) you can workaround this by calling

/bin/stty raw

in terminal or by calling

system ("/bin/stty raw");

in your program. This will cause getchar to immediately return the input character to you.

Dont forget to reset the tty behaviour by calling

/bin/stty cooked

when done!

Here is an example (for Linux):

#include <stdio.h>
#include <stdlib.h>

using namespace std;

int main() {
    system ("/bin/stty raw");
    char c = getchar();
    system ("/bin/stty cooked");
    return 0;
}

Also have a look at this SO Post: How to avoid press enter with any getchar()

Also, as suggested in the comments, have a look here: http://linux.die.net/man/3/termios especially on the command tcsetattr, which should work cross-platform.

Community
  • 1
  • 1
Nidhoegger
  • 4,973
  • 4
  • 36
  • 81
  • You should cite termios man page and the tcsetattr function that can help in configuring terminal IO. – Serge Ballesta Jul 22 '15 at 11:48
  • Very good suggestion. Unfortunately im not familiar with termios. If you are, you could post it as an answer. I have briefly added your suggestion to the answer – Nidhoegger Jul 22 '15 at 11:51
  • I've tried adding the snippets at the start and end of the program. It works! But then, (c=='\n') part doesn't work. Its stuck in the while loop forever. I guess this is expected behaviour as I've gone through the SO Post linked. Is there any way where I can check if the character entered is a newline character? – Jagdish Rapata Jul 22 '15 at 12:00
  • Scrolled through the linked SO post. Added (c=='\r') in one of the clauses to include carriage return character. Now its working as expected. Thanks for answering so fast though! It took minutes to get it resolved! – Jagdish Rapata Jul 22 '15 at 12:11
1

Actually, tcsetattr does not apply to Windows (which is what is commonly referred to in this site as "cross-platform"). However, the question is tagged for Linux, so "cross-platform" is a moot point.

By default the standard input, output and error streams are set to

  • line-buffered (input)
  • block-buffered (output)
  • line-buffered (error)

You can change that using setbuf, but of course will not solve the problem (the answer calls for single-character input). POSIX terminal I/O (termios) lets you change via a system call any of the flags shown using stty. As a rule, you might call stty directly from a script, rarely from a C program.

Reading a single character is a frequently asked question, e.g.,

You could also use ncurses: the filter function is useful for programs that process a command-line (rather than a full-screen application). There is a sample program in ncurses-examples (filter.c) which does this.

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
  • This was pretty useful. I did go through the documentation of tcsetattr and I guess being cross-platform it meant for all versions of linux which I think it should work for. Doesn't look like there's a true cross-platform solution unless some external libraries are used? Or can we have multiple code blocks for controlling this behaviour depending on the OS the program is running on and use pre-processor directives to control what get's called depending on the implementation on which the program is running? What would be the right way to do it? – Jagdish Rapata Jul 23 '15 at 12:25
  • There are a variety of approaches, e.g., using a higher-level interface to hide the implementation details. Programs with a lot of ifdef's are hard to maintain (putting complete implementations in separate source files but using common headers to enforce interface consistency is a way to solve that problem). – Thomas Dickey Jul 24 '15 at 01:13