2

I have this C code:

#include "stdio.h"

main()
{
    struct books
    {
        char name[100],author[100];
        int year,copies;
    }book1,book2;

    printf("Enter details of first book\n");
    gets(book1.name);
    gets(book1.author);
    scanf("%d%d",&book1.year,&book1.copies);

    printf("Enter details for second book\n");
    gets(book2.name);
    gets(book2.author);
    scanf("%d%d",&book2.year,&book2.copies);

    printf("%s\n%s\n%d\n%d\n",book1.name,book1.author,book1.year,book1.copies);
    printf("%s\n%s\n%d\n%d\n",book2.name,book2.author,book2.year,book2.copies);  
}  

What is happening here is that it only scans till the author name of the second book. After that it directly prints the output.

Here is my input:(The first two lines are the initial printf statements)

Enter details of first book
warning: this program uses gets(), which is unsafe.
the c programmign laguagne
dfadsda
3432
23
Enter details for second book
ruby on rails
mark hammers  

After which it directly prints the output:

the c programmign laguagne
dfadsda
3432
23

ruby on rails
0
0

What is wrong here? Also we can see that the name of the second book is assinged to the author.

I'm using gcc as the compiler on Mac OS X ML.

5 Answers5

1

Use fflush(stdin) before each input statement. This method will clear the input buffer. After the modification your code will be-

#include "stdio.h"

int main()
{
    struct books
    {
        char name[100],author[100];
        int year,copies;
    }book1,book2;

    printf("Enter details of first book\n");
    gets(book1.name);
    fflush(stdin);

    gets(book1.author);
    fflush(stdin);

    scanf("%d%d",&book1.year,&book1.copies);
    fflush(stdin);

    printf("Enter details for second book\n");
    gets(book2.name);
    fflush(stdin);

    gets(book2.author);
    fflush(stdin);
    scanf("%d%d",&book2.year,&book2.copies);

    printf("%s\n%s\n%d\n%d\n",book1.name,book1.author,book1.year,book1.copies);
    printf("%s\n%s\n%d\n%d\n",book2.name,book2.author,book2.year,book2.copies);  
    return 0;
} 

You can see the details about fflush() here.

UPDATED : Here after the scanf() statement you need to flush the input buffer. The fflush() method is not useful here because it is defined only for output streams. You can consume the rest of a partially-read line with a single line code after each scanf() line, like -

while((c = getchar()) != '\n' && c != EOF);

Than your code will be:

#include "stdio.h"

int main()
{
    struct books
    {
        char name[100],author[100];
        int year,copies;
    }book1,book2;
    char c;
    printf("Enter details of first book\n");
    gets(book1.name);
    gets(book1.author);

    scanf("%d%d",&book1.year,&book1.copies);
    while((c = getchar()) != '\n' && c != EOF);

    printf("Enter details for second book\n");
    gets(book2.name);
    gets(book2.author);
    scanf("%d%d",&book2.year,&book2.copies);
    while((c = getchar()) != '\n' && c != EOF);

    printf("%s\n%s\n%d\n%d\n",book1.name,book1.author,book1.year,book1.copies);
    printf("%s\n%s\n%d\n%d\n",book2.name,book2.author,book2.year,book2.copies);  
    return 0;
} 

OUTPUT :

Enter details of first book
warning: this program uses gets(), which is unsafe.
sadsadas
asa
12
34
Enter details for second book
zxczxc
sds
23
22
sadsadas
asa
12
34
zxczxc
sds
23
22
ritesh
  • 2,245
  • 2
  • 25
  • 37
  • Did you execute this program and see if its working? You shouldn't use fflush for stdin. Read [this](http://c-faq.com/stdio/stdinflush.html) and also [this](http://stackoverflow.com/questions/2979209/using-fflushstdin) – smRaj Oct 15 '13 at 10:19
1

In your source code,

scanf("%d%d",&book1.year,&book1.copies);

does not read the "\n" after "23" because this just reads two integers.

One solution for this problem is to do gets() before reading the second book, like:

#include "stdio.h"

main()
{
    struct books
    {
        char name[100],author[100];
        int year,copies;
    }book1,book2;

    printf("Enter details of first book\n");
    gets(book1.name);
    gets(book1.author);
    scanf(" %d %d",&book1.year,&book1.copies);
    char a[100];
    gets(a);

    printf("Enter details for second book\n");
    gets(book2.name);
    gets(book2.author);
    scanf("  %d  %d",&book2.year,&book2.copies);

    printf("%s\n%s\n%d\n%d\n",book1.name,book1.author,book1.year,book1.copies);
    printf("%s\n%s\n%d\n%d\n",book2.name,book2.author,book2.year,book2.copies);  
}  

For this reason, reading integer using gets and using atoi after that is simpler method.

#include "stdio.h"

main()
{
    struct books
    {
        char name[100],author[100];
        int year,copies;
    }book1,book2;

    printf("Enter details of first book\n");
    gets(book1.name);
    gets(book1.author);
    char buff[100];
    gets(buff);
    book1.year = atoi(buff);
    gets(buff);
    book1.copies = atoi(buff);

    printf("Enter details for second book\n");
    gets(book2.name);
    gets(book2.author);
    gets(buff);
    book2.year = atoi(buff);
    gets(buff);
    book2.copies = atoi(buff);

    printf("%s\n%s\n%d\n%d\n",book1.name,book1.author,book1.year,book1.copies);
    printf("%s\n%s\n%d\n%d\n",book2.name,book2.author,book2.year,book2.copies);  
}  
nhirokinet
  • 21
  • 2
  • In both the methods I will need to provide an extra input for the extra `gets()`(right?) Which I don't want. –  Oct 15 '13 at 08:57
  • @xmpirate the `gets()` of the second example is not an extra code but an alternative to `scanf()`. But if you even don't want this one, you may just change the format string of first `scanf()` to `%d%d\n`, although this does not output the prompt to input the second book. – nhirokinet Oct 15 '13 at 09:10
  • @xmpirate then you can just use second example. If you want to use `scanf` format, you can use `sscanf` to parse the `buff`. – nhirokinet Oct 15 '13 at 09:13
1

Solution:

#include <stdio.h>  /* Using fgets(), scanf(), printf() in this program */
#include <string.h> /* Using strlen() in this program */

int main()
{
    struct books
    {
        char name[100],author[100];
        int year,copies;
    }book1,book2;

    char c;
    char read_new_line;

    printf("Enter details of first book\n");
    if (fgets(book1.name, sizeof(book1.name), stdin) == NULL)
    {
        fprintf(stderr, "error reading name of book 1\n");
        return -1;
    }
    /* Strip out \n character added by fgets */
    book1.name[strlen(book1.name) - 1] ='\0';

    if (fgets(book1.author, sizeof(book1.author), stdin) == NULL)
    {
        fprintf(stderr, "error reading author of book 1\n");
        return -1;
    }
    /* Strip out \n character added by fgets */
    book1.author[strlen(book1.author) - 1] ='\0';

    scanf("%d %d",&book1.year,&book1.copies);
    /* Strip out \n character left out in input stream */
    while ((read_new_line = getchar()) != EOF && read_new_line != '\n')                                             
        ;

    printf("Enter details for second book\n");
    if (fgets(book2.name, sizeof(book2.name), stdin) == NULL)
    {
        fprintf(stderr, "error reading name of book 2\n");
        return -1;
    }
    /* Strip out \n character added by fgets */
    book2.name[strlen(book2.name) -1 ] = '\0';

    if (fgets(book2.author, sizeof(book2.author), stdin) == NULL)
    {
        fprintf(stderr, "error reading author of book 2\n");
        return -1;
    }
    /* Strip out \n character added by fgets */
    book2.author[strlen(book2.author) - 1] ='\0';

    scanf("%d %d",&book2.year,&book2.copies);
    /* Strip out \n character left out in input stream */
    while((c = getchar()) != '\n' && c != EOF)
        ;

    printf("%s\n%s\n%d\n%d\n",book1.name,book1.author,book1.year,book1.copies);
    printf("%s\n%s\n%d\n%d\n",book2.name,book2.author,book2.year,book2.copies);  
    return 0;
}

Observation on code posted in the question:

Lets try to understand why your code is not working:

After the call to scanf from below statement

scanf("%d%d",&book1.year,&book1.copies);

Your input is

3432\n
23\n

scanf reads in 3432 and stores in &book1.year, following \n gets left out in input stream. Then, second %d discards leading whitespaces (whitespaces in this context includes spaces, tabs, new line, etc.) and reads in 23 and stores that in &book1.copies, following \n gets left out in input stream.

when gets(book2.name) is called \n left out in input stream matches gets() criteria and hence 'empty string' is assigned to book2.name and whatever is meant and user input provided for book2.name is stored in book2.author.

Followed by whatever string meant for book2.author and typed as user input is assigned to book2.year%d conversion is done to that and it fails, as no proper integer entered and scanf() returns failed.

Note :

smRaj
  • 1,246
  • 1
  • 9
  • 13
0

Just a small note, you should probably use fgets() instead of gets() since it's now deprecated due to buffer safety issues.

And it's due to the fact that scanf() will eat that last \n before it gets to read the data for the next entry.

yamafontes
  • 5,552
  • 1
  • 18
  • 18
  • how do I use `fgets()`? –  Oct 15 '13 at 08:15
  • `fgets()` takes 3 params. first one is your string / char array, second is the number of characters to be copied, third one is the stream that you're reading from. so something like `fgets(buffer, 20, stdin)` will copy 20 characters from stdin into your buffer array. – yamafontes Oct 15 '13 at 08:18
  • But I'm getting integer values from `scanf()`. How can I get the same from `fgets()`? I'm able to get strings using `fgets()`. –  Oct 15 '13 at 08:38
  • Oh, oops..yeah you're probably going to need `sscanf()` (double s) It's used pretty similarly to `fgets()` http://www.cplusplus.com/reference/cstdio/sscanf/ – yamafontes Oct 15 '13 at 08:42
0

try this instead

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

int main()
{
    struct books
    {
        char name[100],author[100];
        int year,copies;
    }book1 = { 0 },book2 = { 0 }; // initialize to 0

    printf("Enter details of first book\n");
    printf( "name>" ); 
    fgets(book1.name, sizeof(book1.name), stdin);
    // remove \n
    book1.name[strlen(book1.name)-1] = '\0';

    printf( "author>"); 
    fgets(book1.author, sizeof(book1.author), stdin);
    book1.author[strlen(book1.author)-1] = '\0'; // remove \n
    printf( "year copies>");
    scanf("%d %d",&book1.year,&book1.copies);
    fflush(stdin); // remove any garbage remaining like \n

    printf("Enter details for second book\n");
    printf( "name>" );
    fgets(book2.name, sizeof(book2.name), stdin);
    book2.name[strlen(book2.name)-1] = '\0';

    printf( "author>"); 
    fgets(book2.author, sizeof(book2.author), stdin);
    book2.author[strlen(book2.author)-1] = '\0';
    printf( "year copies>");
    scanf("%d %d",&book2.year,&book2.copies);
    printf("%s\n%s\n%d\n%d\n",
      book1.name,book1.author,book1.year,book1.copies);
    printf("%s\n%s\n%d\n%d\n",
      book2.name,book2.author,book2.year,book2.copies);  
    return 0;
}  
AndersK
  • 35,813
  • 6
  • 60
  • 86