Getchar() problem


Joined
Jan 2, 2022
Messages
5
Reaction score
0
I am working my way through section 1.5.1 of K&R C second addition (which is 89C I believe). This is the program that is for "file copying."

I am using the latest version of debian and the gcc from the standard repository. Although, I modified stty so it sends the EOF signal. And I am writing it for C99 rather than watch the compiler complain.

My Code reads:

#include<studio.h>

void main ()
{
int c;
c=getchar();
while(c != EOF) {
putchar(c);
c = getchar();
}
}

The output is a flashing cursor that allows input.
I write: "The dog is black and white." Or any length of string. Nothing happens
Upon pressing return the output is the entire string.
The EOF signal exits the program.

I have read c99 compliant references and it appears to me it should output a single letter rather than the entire string. Can anyone verify that for me or offer some insight as to why I'm getting such bizarre behavior?
 
Ad

Advertisements

Joined
Nov 13, 2020
Messages
108
Reaction score
17
while(c != EOF) {
putchar(c);
c = getchar();
}
Is a loop that gets every letter until EOF. So every letter you type. If you only want the first char don't use a loop.Replace the entire loop with "putchar(c);".
 
Joined
Jan 2, 2022
Messages
5
Reaction score
0
Thank you.
I guess putchar is called by the newline character which is why it is not executed during the loop each time?
 
Joined
Nov 13, 2020
Messages
108
Reaction score
17
Forget my first post.
The first c=getchar(); contains the first letter, which you want to output. Preserve it by adding "int f;" and then "f = c;" then after the loop output you could output the first letter with "putchar(f);"

Additionally, you could print a string to say it was the first character. As an additional exercise, you could try outputting the fifth character, then for an additional exercise, let the writer choose which character to output. Have him do that before he writes. Then, for a harder test have him enter the number after he writes. This one will have you working with strings so it might be too advanced.
 
Joined
Mar 3, 2021
Messages
174
Reaction score
19
This is to be expected because the terminal and I/O streams are buffered and newlines trigger the buffers to be flushed. I tried using setvbuf to disable it, but it didn't work (I'm not sure why). If you're good with a POSIX requirement, you can use tcsetattr to accomplish this. This'll do it, tested on Fedora 33 and Ubuntu 20.04. For some reason, on Fedora if you type too fast you'll get EOF in between keystrokes. Ubuntu's on a different terminal, though, so maybe that's the difference.

C:
#include <stdio.h>
#include <termios.h>
#include <unistd.h>


void main(){
        struct termios old_terminal_settings, new_terminal_settings;

        tcgetattr(STDIN_FILENO, &old_terminal_settings);
        new_terminal_settings = old_terminal_settings;
        new_terminal_settings.c_lflag &= ~ICANON;

        tcsetattr(STDIN_FILENO, TCSANOW, &new_terminal_settings);

        int c = getchar();
        while (c != EOF) {
                putchar(c);
                c = getchar();
        }

        tcsetattr(STDIN_FILENO, TCSANOW, &old_terminal_settings);
}
 
Joined
Jan 2, 2022
Messages
5
Reaction score
0
Forget my first post.
The first c=getchar(); contains the first letter, which you want to output. Preserve it by adding "int f;" and then "f = c;" then after the loop output you could output the first letter with "putchar(f);"

Additionally, you could print a string to say it was the first character. As an additional exercise, you could try outputting the fifth character, then for an additional exercise, let the writer choose which character to output. Have him do that before he writes. Then, for a harder test have him enter the number after he writes. This one will have you working with strings so it might be too advanced.
Thank you very much for your help. I read your reply the day you posted it, but didn't take the time to reply.
I've done some more experimentation with purchar() including having it insert a character of my choosing. It's very interesting.
 
Ad

Advertisements

Joined
Jan 2, 2022
Messages
5
Reaction score
0
Forget my first post.
The first c=getchar(); contains the first letter, which you want to output. Preserve it by adding "int f;" and then "f = c;" then after the loop output you could output the first letter with "putchar(f);"

Additionally, you could print a string to say it was the first character. As an additional exercise, you could try outputting the fifth character, then for an additional exercise, let the writer choose which character to output. Have him do that before he writes. Then, for a harder test have him enter the number after he writes. This one will have you working with strings so it might be too advanced.
Thank you very much for your reply. I've continued to experiment with with getchar() putchar(). And attempted your suggestions and a few others. I don't know c well enough to play with strings yet. I'll get there though.
 
Joined
Jan 2, 2022
Messages
5
Reaction score
0
This is to be expected because the terminal and I/O streams are buffered and newlines trigger the buffers to be flushed. I tried using setvbuf to disable it, but it didn't work (I'm not sure why). If you're good with a POSIX requirement, you can use tcsetattr to accomplish this. This'll do it, tested on Fedora 33 and Ubuntu 20.04. For some reason, on Fedora if you type too fast you'll get EOF in between keystrokes. Ubuntu's on a different terminal, though, so maybe that's the difference.

C:
#include <stdio.h>
#include <termios.h>
#include <unistd.h>


void main(){
        struct termios old_terminal_settings, new_terminal_settings;

        tcgetattr(STDIN_FILENO, &old_terminal_settings);
        new_terminal_settings = old_terminal_settings;
        new_terminal_settings.c_lflag &= ~ICANON;

        tcsetattr(STDIN_FILENO, TCSANOW, &new_terminal_settings);

        int c = getchar();
        while (c != EOF) {
                putchar(c);
                c = getchar();
        }

        tcsetattr(STDIN_FILENO, TCSANOW, &old_terminal_settings);
}
You are several steps ahead of my investigation. I had read about /n functionality in "the complete reference: C 4th addition" by Herbert Schildt. It read: "For many compilers, getchar( ) is implemented in such a way that it buffers input until ENTER is pressed." He wrote a program that interactively left the loop on "." Keypress within the same line. My programs currently have to be at the beginning of a new line when the EOF is sent.

I wouldn't say I'm particularly good at anything. Could you point me in the directions of some book subjects to read so I could better understand your suggestions and code?

i appreciate your help by the way.
 
Ad

Advertisements

Joined
Mar 3, 2021
Messages
174
Reaction score
19
Can't say much for books, I'm mostly a trial-and-error taught programmer. For this particular case, I'd just go the termios man page, particularly the section labeled "Canonical and noncanonical mode." But, a few bullet facts for the code sample.

- STDIN_FILENO is the (numeric) file descriptor of STDIN. STDOUT also has one.
- tcsetattr/tcgetattr set and get terminal attributes.
- TCSANOW tells it to apply the changes now (as opposed to after flushing or the other modes available).
- ~ICANON is setting it to non-canonical mode (&= ~ means to AND the integer with the bit flipped (~) value of ICANON, which means just unset that one bit)

Other than that, we're just setting it to non-canonical, doing your work, then setting the terminal back to how it was before.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top