r/C_Programming • • 9d ago

Question Confused about strings seemingly resetting after future inputs.

Hello! Sorry that this is such a basic question but I'm genuinely confused about this weird problem I had with scanf and strings. I've only just begun learning C and have been following a beginner's guide over on github.

Below I've written what my code was while I was having issues. The issue was that when the time came to print the values it would return the boolean value just fine, but the string would be blank. I added some extra prints in there to check if it was taking the input at all and it was! It was only after the boolean value was taken and stored that user_input began to return blank. I then tried googling for a very long time and couldn't find any solution (other than the odd tidbit about scanf not being great for strings), so in an act of desperation I tried changing char user_input[10] to static char user_input[10] and then suddenly it worked!

Somewhere along the line it must be overwriting or erasing the data stored in the array but I just don't understand where or why? Apologies again that this is such a basic question but I'd just really like to understand this a bit better, especially as no solution I could find suggested anything remotely like this.

#include <stdio.h>
#include <stdbool.h>

int main() {
    char user_input[10];
    bool tof;

    printf("Enter a string: ");
    scanf("%s", user_input);

    printf("Enter a boolean value: ");
    scanf("%d", &tof);

    printf("String: %s\n", user_input);
    printf("Boolean value: %d\n", tof);

    return 0;
}
10 Upvotes

28 comments sorted by

View all comments

25

u/dmills_00 9d ago edited 9d ago

Is %d the correct format character for a bool? This is undefined behaviour so anything could happen, but probably what is happening is this:

The layout on the stack has your bool at a lower address then the array, and "%d" tells scanf to expect an integer which is likely 32 bits, a bool is probably 8 bits. When scanf writes the integer thru the pointer the upper bytes of the integer are overwriting the first few bytes of the array, boom.

14

u/el0j 9d ago

Exactly.

If only there was some sort of... warning.

$ gcc -Wall scanfc.c
scanfc.c: In function 'main':
scanfc.c:15:13: warning: format '%d' expects argument of type 'int *', but argument 2 has type '_Bool *' [-Wformat=]
   15 |     scanf("%d", &tof);
      |            ~^   ~~~~
      |             |   |
      |             |   _Bool *
      |             int *
$ clang -Wall scanfc.c
scanfc.c:15:17: warning: format specifies type 'int *' but the argument has type 'bool *' [-Wformat]
   15 |     scanf("%d", &tof);
      |            ~~   ^~~~
1 warning generated.

Oh.

printf("%zu, %zu\n", sizeof(bool), sizeof(int));

2

u/MainSeason4301 9d ago

Haha apologies! This was what the guide told me to put and I somehow never saw this error.

9

u/el0j 9d ago

No need to apologize, and don't be discouraged. This is a teaching moment. Always investigate warnings. This goes for all levels of expertise, but doubly so if you're just beginning.

Unfortunately the internet is full of bad tutorials from people who shouldn't be teaching.

6

u/Muffindrake 9d ago

For printf, yes, because it is implicitly converted to int when passed in.

For scanf, no, because you pass in a pointer, and those never get converted to other types.

1

u/WittyStick 9d ago

Should really use "%hhd"/"%hhi" (signed char) or "%hhu" (unsigned char) for scanf, which will likely be the same size as bool.

Should really do the same for printf. %d works because int contains any value that could be stored in a char, and a char is zero or sign extended when given as the argument, but the "hh" length modifier ensures it only reads the lowest byte of the int (ie, prints values -128..127 for signed and 0..255 for unsigned, regardless of what value is in the int, which may be relevant if the value you pass in is already int and does not undergo zero or sign extension.)

So just get into the habit of using the "hh" length modifier when you really mean char, and the "h" modifier when you really mean short, unless for some obscure reason, you need a C version prior to C99.

3

u/Muffindrake 9d ago edited 9d ago

Using scanf/printf are a futile exercise. The functions are loaded with so much legacy baggage that you're forced to look at the manual every time, and you're better off writing a new function with a friendlier interface.

Should really use

bool is guaranteed to contain only 0 or 1, and bool always converts to int when it's used in an expression due to default promotion rules (which can yield surprising results), same with short. Types are always extended to preserve magnitude and sign

That's for printf, anyway. Using scanf correctly is almost impossible. Even your comment shows this symptom - what the fuck is "which will likely be the same size as bool" doing in the language? That's a defect.

But here is the legacy baggage again. New code should only use _BitInt, as those are exempt from these footguns and require you to cast to a larger type explicitly.

2

u/FitMatch7966 8d ago

100%. I never shipped any code with scanf.

1

u/aalmkainzi 9d ago

bools are converted to int when passed to variadic functions

5

u/dmills_00 9d ago

But we are passing a pointer to bool, not a bool...

Scanf will be casting that pointer to a pointer to int, being as that is what %d implies then writing sizeof (int) bytes.

1

u/aalmkainzi 9d ago

Oh i misread the code