1

First question, why when I run this program does it fail to get user data regarding "scanf(" %d", &books[ctr]->pages);" ? Instead of getting user data it restarts the loop.

Second question, can someone explain why getchar(); is used at the end of the first loop. As stated in the book it says "Clears newline input" but that doesn't really make sense to me. Does this have anything to do with the problem I am having.

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

struct bookInfo {
        char title[40];
        char auth[25];
        float price;
        int pages;
};

int main()
{
    int ctr;
    struct bookInfo * books[3];

    //get info on books

    for (ctr = 0; ctr < 3; ctr++)
    {
        books[ctr] = (struct bookInfo*)malloc(sizeof(struct bookInfo));
        printf("What is the name of the book #%d?\n", (ctr+1));
        fgets(books[ctr]->title, 40, stdin);
        puts("Who is the author? ");
        fgets(books[ctr]->auth, 25, stdin);
        puts("How much did the book cost? ");
        scanf(" $%f", &books[ctr]->price);
        puts("How many pages are in the book? ");
        scanf(" %d", &books[ctr]->pages);
        getchar();
    }

    //print new header and then loop through and print info

    printf("\n\nHere is the collection of books: \n");
    for (ctr = 0; ctr < 3; ctr++)
    {
        printf("#%d: %s by %s", (ctr + 1), books[ctr]->title, books[ctr]->auth);
        printf("\nIt is %d pages and costs $%.2f", books[ctr]->pages, books[ctr]->price);
        printf("\n\n");
    }

    return(0);
}    

I expect this program to gather how many pages are in the book before completing the loop and asking for info regarding the book #2. However....

When I run the program this is what I get...

What is the name of the book #1?

good book

Who is the author? good author

How much did the book cost?

50

How many pages are in the book?

What is the name of the book #2?

bad book

Who is the author?

P.P
  • 117,907
  • 20
  • 175
  • 238
  • Welcome to Stack Overflow! [Please see this discussion on why not to cast the return value of malloc() and family in C..](https://stackoverflow.com/q/605845/2173917) – Sourav Ghosh Dec 04 '17 at 20:37
  • You need to check the return value from `scanf`. – user3386109 Dec 04 '17 at 20:37
  • 2
    `scanf(" $%f",`...are you sure you want that `$` there? – Sourav Ghosh Dec 04 '17 at 20:37
  • Thanks! I will read that other suggested question Sourav. As far as the $ goes I just tried it without it and it worked! Im not sure why the author included the $ in the code at that point. Thanks so much! – Clayton Gosse Dec 04 '17 at 20:45
  • 3
    If the author is mixing methods and not checking where it goes: try a [better book](https://stackoverflow.com/questions/562303/the-definitive-c-book-guide-and-list) – Weather Vane Dec 04 '17 at 20:46
  • 2
    No need for the leading spaces in format strings before `%f` or `%d`; these directives _automatically_ ignore leading whitespace, A leading whitespace directive is only ever needed with `%c`, `%[]`, and `%n`. – ad absurdum Dec 04 '17 at 20:48
  • Noted about the spacing, Thanks. Also I will have to check out those books, thanks for linking me to that. – Clayton Gosse Dec 04 '17 at 20:59
  • 1
    This was a well presented question, hope you go far. – Weather Vane Dec 04 '17 at 21:45
  • regarding: `scanf(" %d", &books[ctr]->pages);` when calling any of the `scanf()` family of functions, always check the returned value (not the parameter values) to assure the operation was successful. In the current scenario, the returned value should be `. – user3629249 Dec 06 '17 at 14:39
  • regarding: `books[ctr] = (struct bookInfo*)malloc(sizeof(struct bookInfo));` when calling any of the heap allocation functions: (malloc, calloc, realloc) 1) always check (!=NULL) the returned value to assure the operation was successful. 2) the returned type is `void*` which can be assigned to any pointer. Casting just clutters the code, making it more difficult to understand, debug, etc. – user3629249 Dec 06 '17 at 14:41
  • when calling `fgets()`, if the input is not too long, then the trailing newline will also be placed in the input field. So the code should be checking for that newline. If found, replace it with a NUL byte. if not found, then call `getchar()` in a loop to clear the I/O stream `stdin` before prompting for more info from the user. – user3629249 Dec 06 '17 at 14:43
  • regarding: `scanf(" $%f", &books[ctr]->price);` this will not work unless the user entered a `$`, which they probably didn;t – user3629249 Dec 06 '17 at 14:46

1 Answers1

4

The problem here is, you never checked the reurn value of scanf()!!

Thus, following the inputs you have shown, for a scanning statement (i.e., the format string) like

 scanf(" $%f", &books[ctr]->price);

an input of 50 will cause a matching failure. Thus, scanf() will return failure and the input will then remain in the input buffer to be consumed by the next successful call to scanf(). In your case, the immediate next conversion specification seems to match exactly the input, so you get the behavior that scanf() never waits for the user input.

Related, quoting C11, chapter §7.21.6.2/P6,

A directive that is an ordinary multibyte character is executed by reading the next characters of the stream. If any of those characters differ from the ones composing the directive, the directive fails and the differing and subsequent characters remain unread. [....]

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • 2
    Wow what a great community here, I could have been stuck for a while with such a simple problem. Thanks for helping me with my first question here! – Clayton Gosse Dec 04 '17 at 20:55