[SOLVED] Gets() behaving like fgets()?

And here’s another example of what breaking the program looks like:

It doesn’t even seem to exit on the error!

Do you use valgrind? Does it have any output that could help?

No… I’m not sure how to use Valgrind. By the way, I get similar behavior in exercise 25 with MAX_DATA set to 5. I can type in dddddddddd20 to fill all the fields:

If valgrind is installed it’s a simple command for the basic use:

$ valgrind ./ex24

But I usually run with a couple extra switches:

$ valgrind --leak-check=full --track-origins=yes ./ex24

Also if you’ve made it to ex24 without using gdb, I’m impressed.

Thanks so much, I’m installing Valgrind right now and then I’ll try that.

I’ve used a little GDB, but I’m still learning. No Valgrind, though. It seems to be happy…

EDIT: I get the same response with your flags

Worth a shot. Sometimes it catches some weird memory runoff’s and tracks em down. I’m a student too. But gdb and valgrind are both very valuable. gdb is especially awesome once you learn a few commands like explore, print, break, info, watch, make, run, start, step, next, continue, finish, set, delete, clear.

EDIT: I could still be forgetting some. But those all have been good to me in general gdb usage.

Yeah, break is SUPER useful I’ve found. Here’s the output of Valgrind -> [|]

Thanks for the help so far!

I’d say it would be well worth your time at this point to play around in gdb, use the help and apropos commands to get familiar with gdb commands. The more you dabble the more you see what I mean. That gdb is very powerful if you know your way around.

Ok, thank you. I will do that.

As for the issue with fgets, that’s beyond me lol. I’m not to that chapter yet. But I’m sure someone can shine some light on things.

1 Like

That’s super bizarre. The man page says that fgets will add the \0 terminator if the fgets does NOT have an error. It looks like one of three things:

  1. The you.first_name is not actually MAX_DATA in length.
  2. Your structs are not zeroed out so you have a good base. Honestly this shouldn’t matter since fgets is supposed to add the \0 but do that anyway and see if that solves the problem.
  3. This might be the situation where fgets is told to read MAX_DATA -1, it doesn’t read the \n by that byte count, so then it refuses to add the \0. I’d have to research more if that’s a thing but I vaguely remember that.

Try #2, as a quick fix. Remember you can easily zero out a struct by setting one attribute to 0 or NULL on initialization:

struct MyThing stuff = {.name = NULL};

You’ll have to forgive me if that syntax is off since I’m a little rusty on C these days. If none of that works and you’re using malloc to make the data structs then use calloc instead. Do NOT try to use memset to zero out the memory. That produces errors and you’ll get the size wrong all the time.

2 Likes

Thanks! I tried initializing the strings with { 0 } but that didn’t seem to do anything… Initializing them with 0 and NULL didn’t work either. It may very well be the third thing you said. I noticed that the minimum number of characters needed to overflow is 8, not 10 (which is the value of MAX_DATA). Any ideas how I might fix that? I’ll try some things meanwhile. I’m also pretty sure it’s a problem with input, because it doesn’t actually seem to be overflowing in memory, and Valgrind is happy as a clam…

UPDATE: Telling fgets() that the size is MAX_DATA - 2 didn’t work :frowning:

Check this out, maybe it’ll clarify the string initialization?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char **strings;

int main(int argc, char *argv[])
{
    strings = calloc(100, sizeof(char) * 100);
    strings[0] = calloc(100, sizeof(char));
    strings[0] = strncpy(strings[0], "Something\n", 100);

    strings[1] = calloc(100, sizeof(char));
    strings[1] = strncpy(strings[1], "Something else\n", 100);
    
    int i = 0;
    for (i = 0; i < 100; i++) {
        printf(strings[i]);
        free(strings[i]);
    }
    free(strings);
}

I did some research and the problem doesn’t seem to be what I originally thought. fgets() is correctly reading in the string up to MAX_DATA and is correctly terminating it with '\0', but the excess characters I typed are being stored in some buffer and entered into the next fgets() calls.

I was able to solve it by checking whether the input contained "\n" and if so flushed STDIN. Here’s a snippet of the code:

// ...
void flush_stdin() {
  int ch;
  ch = fgetc(stdin);
  do { ch = fgetc(stdin); } while (ch != EOF && ch != '\n');
}

int main(int argc, char *argv[]) {
    Person you = { .age = 0 };
    int i = 0;
    char *in = NULL;

    printf("What's your first name? ");
    in = fgets(you.first_name, MAX_DATA - 1, stdin);
    check(in != NULL, "Failed to read first name");
    if (!strstr(you.first_name, "\n")) flush_stdin();

    printf("What's your last name? ");
    in = fgets(you.last_name, MAX_DATA - 1, stdin);
    check(in != NULL, "Failed to read last name");
    if (!strstr(you.last_name, "\n")) flush_stdin();

    printf("How old are you? ");
    int rc = fscanf(stdin, "%d", &you.age);
    check(rc > 0, "You have to enter a number");

    printf("What color are your eyes:\n");
    for (i = 0; i <= OTHER_EYES; i++) {
        printf("%d) %s\n", i + 1, EYE_COLOR_NAMES[i]);
    }
    printf("> ");

    int eyes = -1;
    rc = fscanf(stdin, "%d", &eyes);
    check(rc > 0, "You have to enter a number");

    you.eyes = eyes - 1;
    check(you.eyes <= OTHER_EYES && you.eyes >= 0, "Do it right, that's not an option");
// ...

No, I don’t think that’s it either. There is something you’re doing that either doesn’t initialize the variables correctly, OR is pointing fgets at the wrong thing. Try this:

That’s my code for the problem. Take that, compile it and run it just like you’re doing here. If mine works and yours doesn’t then do this:

diff zeds_ex24.c my_ex24.c

That’ll tell you what’s wrong. Then, report back because I am dying out of curiosity.

I have the same original problem with your code as well… so weird! This was the only thing that helped.

That means your computer is super weird 'cause that code works on my machines. I mean, unless something changed in C since then so maybe I should go try it. Can you do this for me:

  1. Take my code.
  2. Run it so you cause the error you see, but write down every tiny step you make.
  3. Write those up in your reply so I can replicate it with my file.
  4. Alternatively, do a video demonstrating.
  5. Tell me your exact OS and compiler versions.

I’ll go try it out.

  1. Set MAX_DATA at the top to 10
  2. Run make ex24
  3. Run ./ex24
  4. Type in dddddddddddddddddddddddddddddddddddddddddddddddddddddddd

OS:
Ubuntu 18.04.1 x86_64

Output of gcc -v:

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 7.3.0-27ubuntu1~18.04' --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-7 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04) 

EDIT: Updated ex22 to ex24

Heres what I get:

bruda@cyborg ~/LCTHW/Lectures/ex24 (git)-[master] % ./ex24
What's your First Name? dddddddddddddddddddddddddddddddddddddddddddddddddddddd
[ERROR] (ex24.c:39: errno: None) You have to enter a number.
What's your Last Name? How old are you? %     
bruda@cyborg ~/LCTHW/Lectures/ex24 (git)-[master] %

MY gcc -v:

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/8.2.1/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: /build/gcc/src/gcc/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --enable-libmpx --with-system-zlib --with-isl --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-gnu-indirect-function --enable-multilib --disable-werror --enable-checking=release --enable-default-pie --enable-default-ssp --enable-cet=auto
Thread model: posix
gcc version 8.2.1 20180831 (GCC)