0

I'm running into some problems in saving a string - a name, for example - into a struct field. I've used gets() and fgets() both, but fgets() isn't working properly either.

I never get the chance to input the first employee name; it skips straight to the employee code and then skips the address too. For some reason, when inputting the second employee, I get to input both the name and code, and then it skips the address again.

Anyone know what I'm doing wrong?

#include <stdio.h>

typedef struct {
    char name[150];
    int code;
    char add[300];
} tEmployee;

int main()
{
    printf("How many employees would you like to register?\n");
    int n;
    scanf("%i", &n);

    tEmployee employee[n];

    for (int i = 0; i < n; i++)
    {
        printf("Name: ");
        gets(employee[i].name);
        printf("Code: ");
        scanf("%i", &employee[i].code);
        printf("Address: ");
        gets(employee[i].add);

        printf("%s\n", employee[i].name);
        printf("%i\n", employee[i].code);
        printf("%s\n", employee[i].add);
     }
     return 0;
}
DarkCygnus
  • 7,420
  • 4
  • 36
  • 59
  • 4
    [Never use gets()](http://stackoverflow.com/q/1694036/10077). – Fred Larson May 13 '16 at 16:23
  • 1
    Remember pressing *two* keys for the `scanf`s? The number and the enter? Enter generates `\n`. `gets`/`fgets` consumes it, thus not waiting for furthur input. The fix? Add `getchar();` after every `scanf`. – Spikatrix May 13 '16 at 16:24
  • `scanf` is difficult to use correctly. I'd be more inclined to use `fgets` into a temporary buffer and then `atoi` or `sscanf` the result, like [Jonathan Leffler](http://stackoverflow.com/a/9562835/10077). – Fred Larson May 13 '16 at 16:37

3 Answers3

1

The C library input routines aren't consistent about the way they handle newline (\n). Some read it as part of the input, some don't. Since scanf() gets what it needs before the newline, it has no reason to read it in so we have to so explicitly to clear it out of the buffer before our next input. There are different techniques but just calling getchar() works for this example.

Also, since gets() is considered unsafe, and leaves a newline dangling on the end of your input, I've added a custom my_gets() wrapper that fixes both issues:

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

typedef struct {
    char name[150];
    int code;
    char add[300];
} tEmployee;

char *my_gets(char *str, int size)
{
    char *pos;

    char *result = fgets(str, size, stdin);

    if (result != NULL && (pos = strchr(str, '\n')) != NULL)
        *pos = '\0';

    return result;
}

int main()
{
    int n;
    printf("How many employees would you like to register?\n");
    scanf("%i", &n);
    getchar(); // eat newline \n

    tEmployee employee[n];

    for (int i = 0; i < n; i++)
    {
        printf("Name: ");
        my_gets(employee[i].name, 150);

        printf("Code: ");
        scanf("%i", &employee[i].code);
        getchar(); // eat newline \n

        printf("Address: ");
        my_gets(employee[i].add, 300);

        printf("%s\n", employee[i].name);
        printf("%i\n", employee[i].code);
        printf("%s\n", employee[i].add);
    }

    return 0;
}

You could make a similar wrapper function for your specific use of scanf() that eats the extra newline for you so you don't have to worry about it everytime you call that function for input.

cdlane
  • 40,441
  • 5
  • 32
  • 81
0

It's your mixed use of gets and scanf. I've faced similar problem in C++, when I mixed the use of std::cin and >> operator and std::getline function.

Also, gets is deprecated, don't use it...

Anyway, if you really want to use the both, then you should "flush" stdin each time you use scanf, or the next time you read stdin you will read the rest of it till the end of line (the \n).

One way to do it, is to read till the end of line after each scanf:

/* define the function */
void flush()
{
    while (getchar() != '\n');
}

Then use it in your code as follow:

printf("How many employees would you like to register?\n");
int n;
scanf("%i", &n);

flush();

tEmployee employee[n];

for (int i = 0; i < n; i++)
{
    printf("Name: ");
    gets(employee[i].name);
    printf("Code: ");
    scanf("%i", &employee[i].code);

    flush();

    printf("Address: ");
    gets(employee[i].add);

    printf("%s\n", employee[i].name);
    printf("%i\n", employee[i].code);
    printf("%s\n", employee[i].add);
 }
 return 0;
Community
  • 1
  • 1
Eekan
  • 71
  • 1
  • 9
0

Try this:

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

typedef struct {
    char name[150];
    int32_t code;
    char add[300];    
} tEmployee;

typedef uint_fast8_t bool_t;

/*****************************************************************************
 * flush stdin... this should be standard but somewhy you need to reinvent
 * it all the time...
 *****************************************************************************/
static inline void flush_stdin()
{
    char ch;

    do {
        ch = getchar();
    } while ((ch != '\n') && (ch != EOF));
}

/*****************************************************************************
 * reads a line of text from a stream.
 *****************************************************************************/
static inline bool_t xio_fgetline(FILE *stream, char *linebuf, size_t szline)
{
    fgets(linebuf, szline, stream);

    // find last character.
    char *lc = linebuf + strlen(linebuf) - 1;

    // the only case when lc is a null is if the program memory
    //     has been altered. In this case, it should crash anyway.
    //     therefore I skip a nullcheck before chomping.

    // chomp linebuf.
    if (*lc == '\n') {
        *lc = 0;
    }

    // string is {0} after chomping.
    if (strlen(linebuf) == 0) {
        return 0;
    }

    return 1;
}

/*****************************************************************************
 * reads a line of text from stdin.
 *****************************************************************************/
static inline bool_t xio_getline(char *linebuf, size_t szline)
{
    return (xio_fgetline(stdin, linebuf, szline));
}

int main(int argc, char **argv)
{
    int32_t n;
    tEmployee *employee = (tEmployee *)0;

    printf("How many employees would you like to register?\n");
    scanf("%i", &n);
    flush_stdin();

    employee = (tEmployee *)malloc(n * sizeof(tEmployee));

    for (int32_t i = 0; i < n; ++i) {
        printf("Name: ");
        xio_getline(employee[i].name, sizeof(employee[i].name));

        printf("Code: ");
        scanf("%i", &employee[i].code);
        flush_stdin();

        printf("Address: ");        
        xio_getline(employee[i].add, sizeof(employee[i].add));

        printf("%s\n", employee[i].name);
        printf("%i\n", employee[i].code);
        printf("%s\n", employee[i].add);
    }

    free(employee);
    return 0;
}
Dmytro
  • 5,068
  • 4
  • 39
  • 50