How to implement gets() or fgets() while using structure pointer?
Asked Answered
C

4

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] $
Cavorelievo answered 15/1, 2014 at 13:7 Comment(6)
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.Antares
not working? Please, be more specific. Does it return some garbage data, or what sort of "not working" is it?Jola
See also this stackoverflow about using fgets() with sscanf() #19364451Antares
@RichardChambers i've updated the question, please have a lookCavorelievo
@Jola i've updated the question, please have a look.Cavorelievo
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()'Cavorelievo
V
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.

Violaceous answered 15/1, 2014 at 13:37 Comment(6)
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.Cavorelievo
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 ith element of your table, and the & takes the address of the id field of this ith element.Violaceous
@DragonX, Take a look at this article which may help you understand the relationship between pointers and arrays denniskubes.com/2012/08/16/the-5-minute-guide-to-c-pointersAntares
"...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"Corroborant
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.Corroborant
@Corroborant Dang, it's been quite a long time since I last used this one. My memory is playing tricks on me. Thanks again :).Violaceous
R
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);
Richella answered 15/1, 2014 at 13:39 Comment(0)
N
1

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

scanf("%d",&(info->Id));
getchar();
Naturally answered 23/5, 2014 at 17:13 Comment(0)
C
-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

Continent answered 3/4, 2015 at 3:39 Comment(1)
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 wayStripper

© 2022 - 2024 — McMap. All rights reserved.