Thread: Infinite printf()

  1. #1
    Registered User
    Join Date
    Aug 2008
    Posts
    55

    Infinite printf()

    This code accept an integer response from input fine, but if you give it a character it will infinitly repeat the last printf statement. If anybody can see why I'd love to hear. Thanks.
    Code:
    #include <stdio.h>
    
    #define STORE_NAME "Sierra Sporting Goods"
    
    int main(void){
    int response = 0;
    
    
    printf("\t%s\n\n", STORE_NAME);
    printf("1 = Add a record\n");
    printf("2 = Report\n");
    printf("3 = Delete a record\n");
    printf("4 = Change a record\n");
    printf("5 = Quit\n");
    
    while(response != 5 ){
    scanf("%d", &response);
    printf("\nplease enter 5 when done\n");
    }
    
    return 0;
    }

  2. #2
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,337
    Works for me. Perhaps you are executing a version of your program that is not this one.
    Mainframe assembler programmer by trade. C coder when I can.

  3. #3
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,666
    scanf returns a result.
    Use it to decide on your level of success at reading an integer.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  4. #4
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Salem View Post
    scanf returns a result.
    Use it to decide on your level of success at reading an integer.
    And, if it fails to read what you expected, you need to "clean up" the input, which is described in the FAQ.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  5. #5
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,337
    Oops - the character I was entering was 5!

    Dino says ditto.
    Mainframe assembler programmer by trade. C coder when I can.

  6. #6
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Something like this should work:
    Code:
    int number;
    
    printf("Enter a number: ");
    while(scanf("&#37;d", &number) != 1) {
        printf("Invalid number, please try again: ");
        while(getchar() != '\n') {}
    }
    
    printf("Number: %d\n", number);
    That doesn't handle EOF properly, though.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  7. #7
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,337
    Here's my verbose solution.

    Code:
    #include <stdio.h>
    
    #define STORE_NAME "Sierra Sporting Goods"
    
    void add_report() { 
    	printf("Report added\n") ; 
    	return ; 
    } 
    
    void report() { 
    	printf("Report run\n") ; 
    	return ; 
    } 
    
    void delete_record() { 
    	printf("record deleted\n") ; 
    	return ; 
    } 
    
    void change_record() { 
    	printf("record changed\n") ; 
    	return ; 
    } 
    
    
    int main(void) {
    	int response = 0;
    	int chars_read ; 
    
    	printf("\t&#37;s\n\n", STORE_NAME);
    	printf("1 = Add a record\n");
    	printf("2 = Report\n");
    	printf("3 = Delete a record\n");
    	printf("4 = Change a record\n");
    	printf("5 = Quit\n");
    
    	while ( response != 5 ) {
    		chars_read = scanf("%d", &response);
    			
    		switch (chars_read) {    // outer switch 
    			case EOF:
    				return -1 ; 
    			case 0: 
    				while (getchar() != '\n') ;  /* clear buffer */ 
    				break ; 
    			case 1: 
    				switch (response) {    // inner switch 
    					case 1: 
    						add_report() ; 
    						break ; 
    					case 2: 
    						report() ; 
    						break ; 
    					case 3: 
    						delete_record() ; 
    						break ; 
    					case 4: 
    						change_record() ; 
    						break ; 
    					case 5:
    						break ;   // allow the while loop to exit 
    					default: 
    						printf("Invalid option %d\n", response) ; 
    						break ; 
    				}  // inner switch  
    				continue ;  // after switch / case 1, 2, 3 or 4 
    			default: 
    				printf("chars_read = %d, Should not occur\n", chars_read) ; 
    				return -1 ; 
    		}   // outer switch
    			
    	} // while response != 5 
    	return 0;
    }
    Mainframe assembler programmer by trade. C coder when I can.

  8. #8
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    No, you have to compare getchar() against EOF as well.

    Here's a loop that does pretty much the same thing as yours.
    Code:
    for(;;) {
        void (*function[])(void) = {
            add_report,
            report,
            delete_record,
            change_record
        }
    
        while(scanf("%d", &response) != 1) {
            int c;
            while((c = getchar()) != '\n' && c != EOF);
            if(c == EOF) break;
            printf("Invalid number, try again: ");
        }
    
        if(response >= 0 && response < sizeof(function) / sizeof(*function)) {
            (function[x])();
        }
        else if(response == 5) break;
        else printf("Enter a number from 0 to 5\n");
    }
    Use a switch if you don't like arrays of function pointers . . . .
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  9. #9
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,337
    dwks, nice concise solution. I like the function pointers. I'm going to follow your lead and come up with a more succinct solution myself. Stay tuned.

    Todd
    Mainframe assembler programmer by trade. C coder when I can.

  10. #10
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    If you wanted to be really fancy, you could use something like this.
    Code:
    struct {
        const char *description;
        void (*function)();
    } item[] = {
        {"Add a record", add_record},
        /* ... */
    };
    Then you could print the descriptions like this:
    Code:
    for(size_t x = 0; x < sizeof(item) / sizeof(*item); x ++) {
        printf("&#37;d = %s\n", (int)x, item[x].description);
    }
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  11. #11
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,337
    That's exactly what I'm working on - still working on the implementation...
    Code:
    { 
    { 1, "Add a Report",	add_report	} , 
    { 2, "Report",			report	} , 
    { 3, "Delete a record", delete_record	} , 
    { 4, "Change a record", change_record	} , 
    { 5, "Quit",			quit	}  
    }
    Mainframe assembler programmer by trade. C coder when I can.

  12. #12
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    But just think -- if you use the index in the structure/whatever as the menu item number, then menu items are independent from the menu. The numbers are assigned automatically. You can add an item just by adding a line like this.
    Code:
    {"Delete a report", delete_report},


    I think it's probably just overkill. I can do it in codeform if I want to, but we're trying to help out someone who's a bit alien to C here.
    Nextstopearth
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  13. #13
    Registered User
    Join Date
    Aug 2008
    Posts
    55
    Hey thanks for the replies everyone, that's really awesome.

    So help me to understand this-

    When scanf() fails to read the specified form of input, it leaves the nonconforming input in place to be read the next time. (<-- straight out of "c primer plus"). So the next time it trys to read it will start with the offending input again. Is there a way to get scanf() to discard that character instead of placing it back? Is that what matsp was refering to as "cleaning up the input"?

    Thanks alot guys, just trying to find out exactly what's going on under the hood here.

  14. #14
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,337
    Quote Originally Posted by Nextstopearth View Post
    Hey thanks for the replies everyone, that's really awesome.

    So help me to understand this-

    When scanf() fails to read the specified form of input, it leaves the nonconforming input in place to be read the next time. (<-- straight out of "c primer plus").
    Yes.

    Quote Originally Posted by Nextstopearth View Post
    So the next time it trys to read it will start with the offending input again.
    Yes.

    Quote Originally Posted by Nextstopearth View Post
    Is there a way to get scanf() to discard that character instead of placing it back?
    scanf()? No.

    Quote Originally Posted by Nextstopearth View Post
    Is that what matsp was refering to as "cleaning up the input"?
    Yes, but you can't via scanf(). See the getchar() code above for how to do that.
    Mainframe assembler programmer by trade. C coder when I can.

  15. #15
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,337
    Here's my succinct version. Thanks for the tips dwks.

    Code:
    #include <stdio.h>
    
    #define STORE_NAME "Sierra Sporting Goods"
    
    void add_report() { 
    	printf("Report added\n") ; 
    	return ; 
    } 
    
    void report() { 
    	printf("Report run\n") ; 
    	return ; 
    } 
    
    void delete_record() { 
    	printf("record deleted\n") ; 
    	return ; 
    } 
    
    void change_record() { 
    	printf("record changed\n") ; 
    	return ; 
    } 
    
    int scanf_ok(int chars_read, int max, int * choice ) { 
    	if (chars_read == EOF || chars_read == 0 ) return 0 ; 
    	if (*choice < 1 || *choice > max) return 0 ; 
    	return 1 ; 
    } 
    
    
    int main(void) {
    	int response = 0;
    	int chars_read ; 
    	int i ; 
    	struct {
    		const char *text ;
    		void (*function)() ;
    	} item[] = {
    		{ "Add a Report"   ,add_report		} , 
    		{ "Report"         ,report		} , 
    		{ "Delete a record",delete_record	} , 
    		{ "Change a record",change_record	} , 
    		{ "Quit"           ,0			}  
    	} ;
    			
    	while ( 1 ) {
    
    		// Present the menu of options.  
    		printf("\t%s\n\n", STORE_NAME) ;
    		for (i = 0 ; i < ( sizeof(item) / sizeof(*item))  ; i++) { 
    			printf("%d = %s\n", i+1, item[i].text ) ; 
    		}
    
    		// Read the response and validate it and process it 
    		if ( scanf_ok( scanf("%d", &response), sizeof(item) / sizeof(*item) , &response ) ) {
    			if (item[response-1].function == 0) break ; 
    			else item[response-1].function() ; 
    		}
    		// else, tell user to try again.
    		else {
    			printf("Invalid or Non numeric value entered.  Try again.\n") ; 
    			while ( ( chars_read=getchar() ) != '\n' && chars_read != EOF) ;  /* clear buffer */ 
    		}
    
    	} // while 1  
    	return 0;
    }
    Any tips for improvement?
    Mainframe assembler programmer by trade. C coder when I can.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. making it portable.....?
    By ShadeS_07 in forum C Programming
    Replies: 11
    Last Post: 12-24-2008, 09:38 AM
  2. get keyboard and mouse events
    By ratte in forum Linux Programming
    Replies: 10
    Last Post: 11-17-2007, 05:42 PM
  3. segmentation fault upon reload
    By yabud in forum C Programming
    Replies: 8
    Last Post: 12-18-2006, 06:54 AM
  4. Simple C question: user input to repeat a loop
    By evernaut in forum C Programming
    Replies: 2
    Last Post: 11-18-2006, 09:23 AM
  5. Drawing tables in C
    By stanoman in forum C Programming
    Replies: 5
    Last Post: 10-09-2003, 10:14 AM