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

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) 

Yeah, I meant Ex24 with the max data. Edited

According to gdb rc == 0 when it gets to 39. That’s after input was entered.

1 Like

I feel like it’s just something with STDIN. Flushing the buffer by repeatedly getting characters seemed to work.

What’s the value of OTHER_EYES supposed to be when it gets to this point?

4, it’s in the enum at the top.

1 Like

Yea it seems like one of those mysterious C things that remind us of why it’s such a terrible language lol. But we get better as we learn more ways it can mess up. I haven’t gotten to this exercise yet. But overall everything seems clean & clear in zeds code. I get the same bug you do it seems.

1 Like

Yeah… I agree. On the other hand, C is super fast and cross-platform. It’s always interesting finding ways to mess it up, for me it’s the same with other programming languages.

1 Like

That’s the results I get, so I have no idea why @archmaster is getting a crash. Sooooooooooooooo weird. I’m sure there’s some bizarre edge case I don’t know about (since that’s C), but I would suggest that you “fix” fgets by telling it to read MAX_DATA-1 or have your arrays sized at MAX_DATA+1 but tell fgets MAX_DATA. That way there’s always a final \0 (if you calloc or set to 0} and it’ll at least not overrun the end.

Now the next thing is if you tell fgets to read 10, and then you blast it with 100, then the 90 remaining get queued up. That’s why we’re seeing that @lee8oi has here. That’s not really a bug at all.

The second thing you said with the characters queuing up is what I think the problem with mine was. I’m sorry if I wasn’t communicating clearly.

My flush_stdin() function seemed to resolve that, so all’s good. Thanks so much for your help!