1

The below code runs perfectly with scanf(), but i want to input characters along with white space. I have tried gets() and fgets() (commented below), but its not working (its skipping to next iteration in the loop & displaying blank(NULL) during output for Name input used by fgets()). Any idea how to do it?

PS: I've tried fputs() with sample programs, its working fine. But i am little confused while using structure pointer.

#include <stdio.h>
#include <stdlib.h>
struct details {
    uint Id;
    char Name[20];
};

int main() {
    int tot, i;
    struct details *info;
    printf("Enter no. of details to be stored = ");
    scanf("%d", &tot);
    info = (struct details *)malloc(tot*sizeof(struct details));

    for(i=0; i<tot; i++) {
        printf("%d)Id = ", i+1);
        scanf("%d", &info->Id);
        printf("%d)Name = ", i+1);
        fgets(info->Name, 20, stdin); //How to make fgets() work??
        //scanf("%s", &info->Name); //works fine with scanf() 
        info++;
    }

    for(i=0; i<tot; i++) {
    --info;
    }

    printf("\nDisplaying details:\n");
    for(i=0; i<tot; i++) {
        printf("%d)Id = %d\n%d)Name = %s\n", i+1, info->Id, i+1, info->Name);
    info++;
    }

return 0;
}

output:

[xyz@xyz:Temp] $ ./fgets_Struct
Enter no. of details to be stored = 2
1)Id = 111
1)Name = 2)Id = 222
2)Name =
Displaying details:
1)Id = 111
1)Name =

2)Id = 222
2)Name =
[xyz@xyz:Temp] $
DragonX
  • 371
  • 2
  • 6
  • 18
  • when you say it is not working, what do you mean? There is one problem with your loop, you should be using a temporary variable to keep the original address stored in the variable info. – Richard Chambers Jan 15 '14 at 13:13
  • not working? Please, be more specific. Does it return some garbage data, or what sort of "not working" is it? – V-X Jan 15 '14 at 13:13
  • See also this stackoverflow about using fgets() with sscanf() http://stackoverflow.com/questions/19363951/fgets-not-working-after-fscanf – Richard Chambers Jan 15 '14 at 13:18
  • @RichardChambers i've updated the question, please have a look – DragonX Jan 15 '14 at 13:24
  • @V-X i've updated the question, please have a look. – DragonX Jan 15 '14 at 13:25
  • No it doesn't return any garbage data, it just skips to next iteration in the loop. And during output it shows blank(NULL) for the 'fgets()' – DragonX Jan 15 '14 at 13:28

4 Answers4

4

The problem comes from the "%d" scanf. It does not consume the line terminator, so the next read will interpret it as an empty line.

Your info allocation is wrong too. What you should use to allocate the size is not the size of info, but the size of an element pointed to by info.

printf("Enter no. of details to be stored = ");
scanf("%d\n", &tot); // <-- must consume end of line here
info = (struct details *)malloc(tot*
   sizeof(*info)); // <-- use size of the pointed object, not the pointer

Also, tinkering with your info pointer is unnecessary and dangerous.
Something like that would be simpler and safer

for(i=0; i<tot; i++) {
    printf("%d)Id = ", i+1);
    scanf("%d\n", &info[i].Id)); // <-- store input in current info record
                                 // and eat up the line terminator
    printf("%d)Name = ", i+1);
    fgets(info[i].Name,          // <-- idem
          sizeof(info[i].Name),  // fgets will guarantee no buffer overflow
          stdin);
}

printf("\nDisplaying details:\n");
for(i=0; i<tot; i++) {
    printf("%d)Id = %d\n%d)Name = %s\n", i+1, info[i].Id, i+1, info[i].Name);

As for scanf / fgets, in your case (with "%s" format given to scanf), they should both read the input until a new line is entered, spaces included.

EDIT: I said something wrong here, sorry and thanks to chux for correcting me.

scanf ("%s") will indeed stop at the first white space. If you want the whole line, the easiest way is to use fgets. If you really want to use scanf, you'll need a syntax like "%[^\n]%*c".

To make sure your buffer does not overflow if the user types more than 20 characters, you can either

  • use "%19[^\n]%*c" as scanf format (20th character is needed for the '\0' terminator), or
  • use fgets with 20 passed as buffer size (fgets takes care of the terminator, it will read at most 19 characters to be able to add the '\0' without overflowing).

Of course you should use sizeof to compute max buffer size, like for instance:

fgets(info[i].Name, sizeof(info[i].Name), stdin);

Thus you won't have to modify the value if you decide, for instance, to have your buffer size changed to 50 characters.

kuroi neko
  • 8,479
  • 1
  • 19
  • 43
  • Thank you for your answer, but a small doubt: I am using 'struct details *info' which is a structure pointer, & according to my understanding struct pointers can be accessed only by '->'(arrow) operators. But your code in the answer above, uses '.'(dot) operator. info[i].Name or info->Name? Please correct me if i am wrong. – DragonX Jan 15 '14 at 14:18
  • 1
    `info->id` is excactly equivalent to `info[0].id`. `Info[i]` does reference the pointer. It's exactly the same as `*(info+i)` (only in a more readable form!). So `&Info[i].id` is the same as `&((*(info+i).id)`. The brackets reference `info` to give access to the `i`th element of your table, and the & takes the address of the `id` field of this `i`th element. – kuroi neko Jan 15 '14 at 14:21
  • 1
    @DragonX, Take a look at this article which may help you understand the relationship between pointers and arrays http://denniskubes.com/2012/08/16/the-5-minute-guide-to-c-pointers/ – Richard Chambers Jan 15 '14 at 14:25
  • 1
    "...scanf ...with "%s" format ...should both read the input until a new line is entered, spaces included." is not correct. "`%s"` in `scanf()` "Matches a sequence of non-white-space characters" (C11dr §7.21.6.2 12). To use `scanf()` to read until a `'\n'` is encountered, suggest format `"%[^\n]"` or `"%[^\n]%*c"`. The 2nd form consumes the `'\n'` or in OP's case: `"%19[^\n]%*c"` – chux - Reinstate Monica Jan 15 '14 at 15:49
  • 1
    Sorry to ping again: `sizeof(info[i].Name)-1` should be `sizeof(info[i].Name)`, no `-1` needed - I thought so too for a long time. "The `fgets` function reads at most _one less_ than the number of characters specified by `n` ..." and 19 s/b 20 for `fgets()`. `scanf()` with 19 is good. – chux - Reinstate Monica Jan 15 '14 at 16:12
  • @chux Dang, it's been quite a long time since I last used this one. My memory is playing tricks on me. Thanks again :). – kuroi neko Jan 15 '14 at 16:14
2

First, you have a serious bug in your code.

info = (struct details *)malloc(tot*sizeof(info));

sizeof(info) returns either 4 or 8 (depending on the platform, 32 or 64 bit), but has nothing to do with your struct size. So you are allocating memory for pointers, but are using that space for your struct. Writing your data into this will overwrite other data in memory (although probably not in that simple example), because you allocate way too few bytes. What you want to use is sizeof(struct details) which would return 24.

Now to your input problem. Basically, scanf and gets should not be used together like that. The problem is that scanf reads what you type until you press return, which is not included. When you call gets after that, it sees this return and returns immediately with an empty string. If you change all your input calls to gets, it works perfectly. Example:

char test[256];
printf("Enter no. of details to be stored = ");
gets(test);
tot = atoi(test);
Thalur
  • 1,155
  • 9
  • 13
1

between the line scanf() for ID and fgets() put the line getchar() like this

scanf("%d",&(info->Id));
getchar();
Ethaan
  • 11,291
  • 5
  • 35
  • 45
kp2349
  • 107
  • 1
  • 14
-1

I had the same problem! Actually, I came to this page to get the solution and then I remembered the buffer!! So this is the way I solved it:

[...]

cout << "\nType the name: " ;
fflush(stdin); (you always have to clean the buffer before saving chars to a variable)
gets(pers.nom);

[...]

Cheers!! P.S. cin and cout are from C++ but work like scanf and printf

Tom Tom
  • 3,680
  • 5
  • 35
  • 40
Ailin
  • 21
  • fflush(stdin) may work under some compilers, but the behavior is not defined by standard, so you should avoid it and clean the input buffer some other way – Jan Turoň Apr 16 '15 at 23:39