3

In the following code example from K&R's book, if I replace putchar(c) with printf("%c", c) the code works the same. But if I replace it with printf("%d", c) it gives gibberish output.

#include <stdio.h>
int main() {
  int c;
  c = getchar();
  while (c != EOF) {
    putchar(c);
    c = getchar();
  }
}

From here, I learned that the getchar() converts the stdin to an 8-bit character whose value ranges from 0 to 255.

Now I want to print the value of c using putchar(c) in one line and printf("%d", c) in another line. So I wrote the following code:

#include <stdio.h>

int main() {
  int c, b;

  c = getchar();
  b = c;
  while (c != EOF && c != 10) {
    printf("%c",c);
    c = getchar();
  }
  printf("\n");
  while (b != EOF && b != 10) {
    printf("%d\t",b);
    b = getchar();
  }
}

I used the condition c != 10 as the newline character is read as 10 by getchar(). I expected the code to work as

$ ./a.out
783
783
55 56 51

but the program terminates as

$ ./a.out
783
783
55

I understand that getchar() takes input from stdin and the variable b is not stdin. So how should I copy the variable c to b so that my program works as I expect it to?

  • 1
    Nit, but using the character literal `'\n'` is much easier to understand when reading (for someone not familiar with the ASCII chart) than `10`. Use `'A'` when you mean `'A'` not `65` or `'a'` when you mean `'a'`, not `97`. Or `'\r'` when you mean `'\r'` instead of `13`, etc... -- makes things much more readable. – David C. Rankin Apr 06 '20 at 03:46
  • As `c` is an `int` variable, I thought of comparing it with an integer. But yes, `'\n'` works as well. – DeusExMachina Apr 07 '20 at 15:12
  • All character functions such as `getchar()` return `int` (which is actually the `unsigned char` value of the character cast to `int`), so you are good just comparing to the character literal in all cases. – David C. Rankin Apr 07 '20 at 21:50

3 Answers3

2

The problem is that your code does not (and cannot, as it stands) 'remember' the inputs you gave in the first loop. So, after you have finished that loop, your second loop is wanting to read in the characters for b (after it has output the first value, which is remembered from the earlier b = c line).

So, after outputting 55 (the integer value of the character 7), it is waiting for further input.

Probably the easiest way to get the output that you're looking for is to have an array of input characters. Then, you can output the %c values as you read them (as before), then re-run the outputs using the %d format in a subsequent for loop.

Here is a demonstration that does what I think you're after:

#include <stdio.h>
#define MAXINS 20 // Set to the maximum number of input characters you want to allow
int main()
{
    int c[MAXINS];
    int i = 0, n = 0;
    c[0] = getchar();
    while (i < MAXINS && c[i] != EOF && c[i] != 10) {
        printf("%c", c[i]);
        c[++i] = getchar();
        ++n;
    }
    printf("\n");
    for (i = 0; i < n; ++i) {
        printf("%d\t", (int)(c[i]));
    }
    return 0;
}

Feel free to ask for further clarification and/or explanation.

EDIT: On the point in the your first paragraph, "But if I replace it with printf("%d", c) it gives gibberish output." Well, when I try the following code and give 783 and then hit return (which generates a newline) I get the expected 55565110 as the output:

int main()
{
    int c;
    c = getchar();
    while (c != EOF) {
        printf("%d", c);
        c = getchar();
    }
    return 0;
}

This may look like gibberish, but it's just the same output as you 'expect' in your later code, but without the spaces and with the addition of the 10 for the newline.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • Ah! I get it. `getchar()` copies only one character from stdin, that's why `c=getchar(); b=c;` copies only one character to `b`. Though you have answered my question completely, I was wondering if there is a way to solve it without using an array. – DeusExMachina Apr 05 '20 at 10:21
  • 1
    Without using complex cursor-positioning stuff, you would *have* to use an array of some sort. You could use two *output* arrays (rather than an input array), writing the outputs (in one loop) to two different strings, then printing them after your loop exits. – Adrian Mole Apr 05 '20 at 10:32
  • @DeusExMachina If you don´t necessarily need to output the character and integer values subsequently with each other and also do not need to have `b`, you can simply use one loop and output it via `printf("%c %d\n",c,c);`. – RobertS supports Monica Cellio Apr 05 '20 at 10:38
2

You need to have every character stored, because once you read a char from stdin, it is not present in stdin anymore.

Since you want the newline character in the end as a part of the input, you should use fgets to take the input.

Say you are taking an input that could have a maximum of 100 characters.

#include <stdio.h>

int main(void) {
    char c[100]; // A char array
    fgets(c,100,stdin);
    int x=0;
    while (c[x] != 10 && c[x] != EOF)
        printf("%c",c[x++]);
    printf("\n");
    x = 0;
    while (c[x] != 10 && c[x] != EOF) // You can simply compare it with the newline character too.
        printf("%d ",c[x++]);
    printf("\n");
    return 0;
}

There are many ways to do this. You can also read stdin character-by-character ans store it in an array. However, since you need to display the ASCII values of the characters in another line after displaying the characters themselves, you will have to store them in an array.

Param Siddharth
  • 858
  • 2
  • 7
  • 20
  • 1
    Yeah, `fgets`is the solution, but it seems that OP is doing some learning-test with `getchar` – David Ranieri Apr 05 '20 at 09:27
  • Correct! And they would still need to store all the characters in an array to be able to display each character's ASCII value after displaying the characters themselves. – Param Siddharth Apr 05 '20 at 09:29
1

You are copying only the first input, to copy the whole string you need to store each input in a buffer and check if the string doesn't overflow that buffer on each iteration:

int main(void)
{
    enum {size = 256};
    char buffer[size];
    size_t count = 0;
    int c;

    while ((c = getchar()) && (c != '\n') && (c != EOF))
    {
        printf("%c", c);
        if (count < size)
        {
            buffer[count++] = (char)c;
        }
    }
    printf("\n");
    for (size_t iter = 0; iter < count; iter++)
    {
        printf("%d\t", buffer[iter]);
    }
    printf("\n");
}

If you don't want to limit the buffer to an arbitrary size then you need to change your approach to use dynamic memory (realloc or a linked list)

David Ranieri
  • 39,972
  • 7
  • 52
  • 94
  • I get the answer. Here I have another question: if I replace `'\n'` of your code by `"\n"`, the compiler gives a warning `warning: comparison between pointer and integer`. Could you please explain why is that? – DeusExMachina Apr 05 '20 at 10:32
  • 1
    @DeusExMachina `'\n'` is as an `int` (with value 10) and can be compared with `c` (also an `int`), `"\n"` is a pointer to `char`, hence the warning, comparing a pointer (an address like 0xdeadbeef) with an `int` doesn't make sense. – David Ranieri Apr 05 '20 at 11:42