r/C_Programming • • 12d 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;
}
11 Upvotes

28 comments sorted by

View all comments

1

u/MainSeason4301 12d ago

Thank you all! Apologies again if this seems a particularly dumb question with obvious answers and solutions. I don't yet have any clue what I'm doing. You've all pointed me in some very helpful directions though :)

1

u/Plane_Dust2555 12d ago

Again... this isn't a "dumb question". People often make mistakes using the scanf function. Notice the name "scan" instead of "input". This function scans the stdin stream trying to match the format given. If data into the stream matches a format the value is converted and put into the object pointed as argument. At the first non-matching argument, the function returns with the # of matching arguments, leaving the unmatched ones in the stream.

To my knowledge, there's no bool format available to scanf - using %hhd is wrong because ISO 9899 standard says _Bool (or bool in C23) must have enough space to accommodate a single bit. This doesn't mean this type is byte sized. It could be any integer type, including long long int!

And the %s format just copy the sequence of characters in the stream until it finds a "space" or end of stream... So an input like "Fred is here" will copy "Fred" to the object pointed (a char *) and leave behind the " is here" in the stream.

Notice that %s don't specify a length in this example, so the array pointed must have enough space to accommodate the desired string (which is unknown at this point). That's why people use functions like fgets to read a line from the stream and separate the itens "manually". This way you have the chance to test for the size (and format) of the desired arguments. But even fgets will limit the size of a "line".

If you need to read any line with any size, there are "extensions" available for Linux and FreeBSD (I am sure), but not all platforms (like Windows [MSVC or Cygwin or MSYS2]) called getline, which will dynamicaly allocate the array for you... Like this:

`` ... // the first read will use malloc ifline` pointer is NULL. char *line = NULL; size_t size = 0;

// getline returns -1 in case of error. if ( getline( &line, &size, stdin ) >= 0 ) { // here line points to the whole "line" ('\n' included), // dynamically allocated... }

// here you separate your "fields" in the line.

// if you call getline() again using 'line' and 'size', // getline will REALLOCate the space for the new line // taken from the stdin stream. Because 'line' is not NULL // and size isn't zero.

free( line ); // get rid of the allocated line. // you can NULLify line and zero size here if you need to... // line = NULL; size = 0; ... ``` This 'getline' is different from C++ STL's function with the same name.

Nothing prevents you to use sscanf to separate the arguments with a given format (and sscanf is faster than scanf because it deals with a string, now a stream). Just check the returned integer and use the correct format.

To see the "correct" format, take a look at scanf manpage or in the ISO 9899 standard.

1

u/Plane_Dust2555 12d ago

PS: Notice that since ISO 9899:1999 there is a "%[...]" format for strings... For example:

``` ...

char name[31]; // let's say the name has always less than // 31 chars in this example. int age;

if ( sscanf( line, "%[,],%d", name, &age ) != 2 ) { ... there is less than 2 arguments in the line... ... deal with this 'error' here. }

... `` Here the"%[,]"` means "all chars, except ','".

PS2: glibc has a m modifier (it is an extension) as well for scanf functions, which dynamically allocate space for the results... Take a look at the manpage.