1

So my code does the following:

  1. Ask what's the option
  2. If option is 1: Scan some numbers
  3. If option is 2: Print those numbers
  4. After each option, ask if user wanted to continue choosing (Y/N)

This is my main code

while(yesnocheck==1)
{
    printf("What's your option?: ");
    scanf("%d",&b);

    switch(b){
        case 1:
            printf("How many numbers?: ");
            scanf(" %d",&n);
            a=(struct sv*)malloc(n*sizeof(struct sv));

            for(int i=0;i<n;i++)
                scanf("%d",&((a+i)->num));
            break;
        case 2:
            for(int i=0;i<n;i++)
                printf("%d\n",(a+i)->num);
            break;
    }
    yesnocheck==yesnochecker();
}

And this is the yesnochecker function:

int yesnochecker()
{
    char yesorno;
    printf("Do you want to continue? (Y/N)");

        while(scanf("%s",&yesorno))
    {
        if(yesorno=='Y')
            return 1;
        if(yesorno='N')
            return 0;   
        printf("*Wrong input. Please reenter (Y/N): ");
    }
}

So on dev C++, my code won't run correctly. After it's done option 1, when I enter "Y" then choose option 2, case 2 will display some weird numbers. However it works well on online C compilers.

And then, when I change the char yesorno in yesnochecker() function to char yesorno[2] and treat it as a string, the code does work.

Can someone shed some light?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • You're scanning wrong. All you want is a character, right? The formatter in `scanf("%s",&yesorno)` is `%c`, not `%s`. – Unmanned Player Aug 30 '18 at 05:42
  • There are lots of programming errors here. Probably you could turn on warnings to see where it's failing. – Unmanned Player Aug 30 '18 at 05:42
  • Note that the `%s` format needs an array of at least 2 characters (one for a choice, one for the null byte) and is vulnerable to overflows (`%1s` would be safer for an array of 2 characters). If you use `" %c"`, you can use a single character. That leading space is not an accident; it is crucial. It skips of white space (such as newlines left over from the last input). Also, your while condition will go into an infinite loop if it encounters EOF before encountering a Y or an N. – Jonathan Leffler Aug 30 '18 at 06:09
  • 1
    Welcome to SO. If you compile and run `char yesorno; scanf("%s", &yesorno);` and you type e.g. `Y` then it stores two characters - the `Y` and `\0` (to terminate the string). Your `char yesorno` provides only storage for one, the second is written to memory which doesn't belong to `yesorno`. This is [Undefined Behavior](https://stackoverflow.com/a/4105123/1505939). Either, you use `char yesorno; scanf("%c", &yesorno);` or you provide sufficient storage. The latter is hard to achieve with `"%s"` as there is no length limitation given. – Scheff's Cat Aug 30 '18 at 06:09
  • Note, too, that the line `yesnocheck==yesnochecker();` is wrong; you need an assignment, not a comparison. – Jonathan Leffler Aug 30 '18 at 06:14
  • Since DevC++ is no longer maintained (and got a horrible debugger), I recommend getting something newer and better such as Codeblocks. – Lundin Aug 30 '18 at 06:32

1 Answers1

1

It is a bad idea to read a char c with scanf("%s", &c);. "%s" requires a buffer to store a string. The only string which fits into a char is an empty string (consisting only of a terminator '\0' – not very useful). Every string with 1 character requires 2 chars of storage – 1 for the character, 1 for the terminator ('\0'). Providing a char for storage is Undefined Behavior.

So, the first hint was to use the proper formatter instead – "%c".

This is better as it removes the Undefined Behavior. However, it doesn't solve another problem as the following sample shows:

#include <stdio.h>

int cont()
{
  char c; do {
    printf("Continue (y/n): ");
    scanf("%c", &c);
    printf("Input %c\n", c);
  } while (c != 'y' && c != 'n');
  return c == 'y';
}

int main()
{
  int i = 0;
  do {
    printf("Loop iteration %d.\n", ++i);
  } while (cont());
  /* done */
  return 0;
}

Output:

Loop iteration 1.
Continue (y/n): y↵
Input 'y'
Loop iteration 2.
Continue (y/n): 
Input '
'
Continue (y/n): n↵
Input 'n'

Live Demo on ideone

WTH?

The scanf("%c") consumes one character from input. The other character (inserted for the ENTER key) stays in input buffer until next call of any input function.

Too bad, without ENTER it is hard to confirm input on console.

A possible solution is to read characters until the ENTER key is received (or input fails for any reasons). (And, btw., getc() or fgetc() can be used as well to read a single character.):

#include <stdio.h>

int cont()
{
  int c;
  do {
    int d;
    printf("Continue (y/n): ");
    if ((c = fgetc(stdin)) < 0) {
      fprintf(stderr, "Input failed!\n"); return 0;
    }
    printf("Input '%c'\n", c);
    for (d = c; d != '\n';) {
      if ((d = fgetc(stdin)) < 0) {
        fprintf(stderr, "Input failed!\n"); return 0;
      }
    }
  } while (c != 'y' && c != 'n');
  return c == 'y';
}

int main()
{
  int i = 0;
  do {
    printf("Loop iteration %d.\n", ++i);
  } while (cont());
  /* done */
  return 0;
}

Output:

Loop iteration 1.
Continue (y/n): y↵
Input 'y'
Loop iteration 2.
Continue (y/n): Hello↵
Input 'H'
Continue (y/n): n↵
Input 'n'

Live Demo on ideone

Please, note, that I changed the type for the read character to int. This is because getc()/fgetc() return an int which is capable to store any of the 256 possible char values as well as -1 which is returned in case of failing.

However, it isn't any problem to compare an int with a character constant (e.g. 'y'). In C, the type of character constants is just int (SO: Type of character constant).

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • A simpler option is to use `” %c”`. The leading space skips optional white space, such as newlines. It doesn’t help if the user is prone to typing “yes” when asked to type “y”. Then a character gobbling loop can help, but the leading space still helps if the user accidentally types a space before the intended character. – Jonathan Leffler Aug 30 '18 at 08:47
  • @JonathanLeffler Thanks for the enlightment. (I use `scanf()` rather never in serious work and easy forget about such details.) Though, leaving unread characters in the input buffer which might cause interferences with separate code (for separate purposes) is something I would feel bad about. Btw. accepting `yes` instead of `y` was something else I had in mind but forgot to mention. (Such a long answer for such a simple topic, isn't it?) ;-) – Scheff's Cat Aug 30 '18 at 08:50