6

I'm writing to understand as much as I can of the following program that I tried to compute, because I thought it had some possible application for everyday life.

The program goes like this (it's a cypher):

#include <stdio.h>

void cifrario(int k) {
    char b, d;
    scanf("%c", &d);
    d = getchar();
    putchar(d);
    scanf("%c", &b);
    b = getchar();
    while (b != d) {
        if (('A' <= b && b <= 'Z') || ('a' <= b && b <= 'z')) {
            b = b + k;
            printf("%c", b);
            scanf("%c", &b);
            b = getchar();
        } else 
            printf("%c", b);
    }
}

int main() {
    int h;
    scanf("%d", &h);
    cifrario(h);
    return 0;
}

As you can see, I'm trying to shift the alphabet of a certain constant given in input.

The things I'm not quite understanding and I'm not quite aware of, are why the program should not function without a scanf or getchar, and both are necessary on the same value, let's say here:

scanf("%c", &d);
d = getchar(); 

I thought the two were totally equivalent; in fact searching on the internet I found this as far as concerns getchar:

This function returns the character read as an unsigned char cast to an int or EOF on end of file or error.

That's what I expected scanf() did.

I have found that:

  • if I put all the character without spaces I'm getting (I think) just a shift of the same character for double letter instead of two.

  • the else in the while doesn't work as expected because as soon I put a different character from the alphabetic ones and see the outcome the output fail in an infinite string of that character.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
jacopoburelli
  • 257
  • 2
  • 9
  • 2
    Newline management sucks with `scanf()`. Just stop using `scanf()` for user input and switch to `fgets()` (possibly followed by `sscanf()`). – pmg Oct 21 '18 at 10:12
  • `'\n'` is a character. – melpomene Oct 21 '18 at 10:18
  • 2
    The return type of `getchar()` is `int` and you are using `char` type variable to hold its return value. – H.S. Oct 21 '18 at 10:38
  • @pmg I'm sorry, i don't know fgets() and sscanf(), are they more useful ? – jacopoburelli Oct 21 '18 at 11:11
  • @H.S. Why is that? I think i initialized all the variables as char – jacopoburelli Oct 21 '18 at 11:12
  • 1
    Yes, they're more useful. `fgets()` allows for error recovery from bad user input, and it makes managing input lines (including the trailing `'\n'`) natural. – pmg Oct 21 '18 at 11:16
  • 2
    @jacopoburelli Read about [getchar()](https://en.cppreference.com/w/c/io/getchar). The `getchar()` return type is `int` because it returns `EOF` on failure. – H.S. Oct 21 '18 at 11:37

1 Answers1

2

Yes, there are some differences.

The main one is behaviour in case that reading a character failed. (Usually this can happen due to the input stream being closed, but there are other reasons for read failure too).

The scanf function indicates failure through its return value. If you do not check the return value (as you don't in your code) then you have no way of knowing whether the read failed or not. In this failure case the d might retain its old value although I am not sure if the standard guarantees that or not.

The getchar() indicates failure by returning EOF, which is a negative integer value (usually -1 but not guaranteed to be). In your code you write d=getchar() which means you cannot distinguish this result from a valid read of a character with the same character code as EOF. (Common systems have characters in the range -128 to 127). The d will never retain its previous value for this code.

In order to correctly cope with errors you either use scanf and test the return value is 1; or store the result of getchar() in an int and check the value is not EOF before going on to assign it to a char.


There is also a theoretical difference in the success case. This is not relevant on modern systems. The scanf version will store the successful character in d. However, the getchar() version returns a non-negative integer which is the character converted to unsigned char. The code d=getchar() then involves converting this value to char. If char is signed this invokes implementation-defined behaviour and it might result in a different character code to what you would have gotten from scanf. I'm not aware of any system that ever existed where this would actually be different however.


Both versions read the next available character from the stream. At the bottom of your question you describe some other behaviour but I think you are confusing the behaviour of scanf with "%d" with that of "%c".

In your program the while (b != d) would never terminate if you never encounter another character the same as d , you need to fix this problem and make sure it terminates when the read fails.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • *this can happen due to the input stream being closed*: not really. If the stream is closed, calling `getchar()` or `scanf()` has undefined behavior. You probably mean if the input stream is at end of file. – chqrlie Oct 22 '18 at 20:42
  • *I am not sure if the standard guarantees that or not.* The standard mentions storing converted values only for successful conversions, hence the target variable should be left unchanged if the conversion fails. – chqrlie Oct 22 '18 at 20:45
  • @chqrlie in practice I wouldn't rely on there being no storage, there's been a lot of buggy stdio implementations over the years. By "stream being closed" I mean closed by the operating system or whatever, in response to conditions such as the input being exhausted. I guess you are talking about the user calling `fclose` – M.M Oct 22 '18 at 20:47
  • The expression *input stream being closed* is somewhat ambiguous: C does refer to `FILE *` objects as *stream pointers*, and they are closed by `fclose()`... You are referring to system handles for pipes and terminal connections which can be closed by external means. These are usually not referred to as *streams*. Regarding implementation quirks and shortcomings, I'm sure your defensive approach is rooted in experience. – chqrlie Oct 22 '18 at 20:59