/*=======================================================================*/ #define PROGRAMMER "SOLUTIONS, Nofrills" #define PROG_CODE "SOLN" #define COURSE "ECE-1021" #define YEAR (2004) #define TERM "Fall" #define SECTION (0) #define ASSIGNMENT "HW6" #define REVISION (0) #define TITLE "Getting Integers from the Keyboard" #define SUBTITLE "Last Modified on 06 SEP 04" #define EMAIL "ece1021@eas.uccs.edu" #define FILENAME "6_0SOLN0.c" /*=======================================================================*/ /*=======================================================================*/ /* WAIVED COMPILER WARNINGS */ /*=======================================================================*/ /* Linker Warning: No module definition file specified: using defaults Reason for Waiver: Can't suppress. Does not have adverse impact. */ /*=======================================================================*/ /* NOTES TO THE USER/GRADER */ /*=======================================================================*/ /* For best results, set the SIZE macro to something between 6 and 30 */ /*=======================================================================*/ /* PROBLEM STATEMENT */ /*=======================================================================*/ /* Write and test the function GetV_u() and GetV_i() that get integer values from the keyboard. Each function takes a pointer as it's sole argument. The first takes a pointer to a variable of type unsigned int while the second takes a pointer to a variable of type int. 1) Each function should skip any leading white space characters. 2) The GetV_i() function should recognize at most one negative sign anywhere within the leading white space group. 3) Each function should then read and convert characters only if they are part of an integer value. 4) Each function should stop upon encountering the first character that it cannot interpret as part of an integer value. 5) The character that caused the read termination should be placed back in the stream buffer ( using ungetc() ). 6) If no characters were successfully read, the value should default to zero. 7) If the pointer passed to the function is not a NULL pointer, then the value read in should be stored at the location pointed to. 8) The function should return the value that was read. */ /*=======================================================================*/ /* DEVELOPMENT NOTES */ /*=======================================================================*/ /* GetV_u() - gets an unsigned integer value from the keyboard GetV_i() - gets an integer value from the keyboard The function headers can be determined by the first part of the problem statement coupled with Behavior #8: unsigned int GetV_u(insigned int *ptr); int GetV_i(int *ptr); The behavior descriptions lead directly to the to level task breakdowns: 1) TASK: Skip any leading whitespace (allow 1 neg sign for GetV_i()) 2) TASK: Convert string of characters to integer as long as possible 3) TASK: Push character that terminated read back into buffer 4) TASK: Write value to memory pointed to by ptr if ptr is not NULL 5) TASK: Return the value that was read in The last three of these tasks have simple and direct solutions: NOTE: Using C syntax in this pseudo-pseudocode for simplicity. 3) TASK: Push character that terminated read back into buffer 1) ungetc(c, stdin); 4) TASK: Write value to memory pointed to by ptr if ptr is not NULL 1) IF: NULL != ptr 1) *ptr = value; 5) TASK: Return the value that was read in 1) return value; Leaving aside the issue of a negative sign till later, the first task: 1) TASK: Skip any leading whitespace (allow 1 neg sign for GetV_i()) 1) LOOP: 1) c = GetC(); 2) WHILE: isspace(c); The central task follows the same flow as base conversion via repeated multiplication: 2) TASK: Convert string of characters to integer as long as possible 1) SET: value = 0; 2) WHILE: isdigit(c) 1) value = 10*value + (c - '0'); 2) c = GetC(); The only thing not addressed by the above is dealing with the negative sign. Since this is not needed for the GetV_u() function, we can implement and test that one at this point. unsigned int GetV_u(unsigned int *ptr) { unsigned int value; int c; TASK: Skip any leading whitespace do { c = GetC(); } while ( ispace(c) ); TASK: Convert string of characters to integer as long as possible value = 0; while ( isdigit(c) ) { value = 10*value + (c - '0'); c = GetC(c); } TASK: Push character that terminated read back into buffer ungetc(c, stdin); TASK: Write value to memory pointed to by ptr if ptr is not NULL if (NULL != ptr) *ptr = value; TASK: Return the value that was read in return value; } The above is a direct translation of the task development. At this point, a few condensations can be made to the loops: unsigned int GetV_u(unsigned int *ptr) { unsigned int value; int c; TASK: Skip any leading whitespace while (ispace(c = GetC())) EMPTYLOOP; TASK: Convert string of characters to integer as long as possible for ( value = 0; isdigit(c); c = GetC() ) value = 10*value + (c - '0'); TASK: Push character that terminated read back into buffer ungetc(c, stdin); TASK: Write value to memory pointed to by ptr if ptr is not NULL if (NULL != ptr) *ptr = value; TASK: Return the value that was read in return value; } The only difference between this and the GetV_i() function is the need to detect a negative sign if one exists within the group of leading whitespace characters. In keeping with the top-down strategy that has been applied in the prior work involving signed and unsigned functions, we will deal with the sign of the value and the magnitude of the value as two separate problems - one of which, namely getting an unsigned value, has already been solved and tested. So the top level task breakdown for GetV_i() becomes something like: 1) TASK: Discard leading whitespace 2) IF: Next Character is a negative sign 1) SET: value = -GetV_u() 3) ELSE: 1) TASK: Push Next Character back into stream buffer 2) SET: value = GetV_u*() 4) TASK: Store value to memory if pointer is not NULL 1) IF: pointer is not NULL 1) SET: *p = value 5) TASK: Return value In converting the above pseudocode to code, a minor reorganization was performed which should be pretty self-explanatory. */ /*=======================================================================*/ /* PSEUDOCODE */ /*=======================================================================*/ /* for GetV_u(): 1) TASK: Skip any leading whitespace (allow 1 neg sign for GetV_i()) 2) TASK: Convert string of characters to integer as long as possible 3) TASK: Push character that terminated read back into buffer 4) TASK: Write value to memory pointed to by ptr if ptr is not NULL 5) TASK: Return the value that was read in for GetV_i(): 1) TASK: Discard leading whitespace 2) IF: Next Character is a negative sign 1) SET: value = -GetV_u() 3) ELSE: 1) TASK: Push Next Character back into stream buffer 2) SET: value = GetV_u*() 4) TASK: Store value to memory if pointer is not NULL 1) IF: pointer is not NULL 1) SET: *p = value 5) TASK: Return value */ /*=======================================================================*/ /* DEVIATIONS FROM SUBMITTED PSEUDOCODE */ /*=======================================================================*/ /* Minor organizational difference in GetV_i(), but identical logic. */ /*=======================================================================*/ /*=======================================================================*/ /* CODE SECTION */ /*=======================================================================*/ /*=======================================================================*/ /*== INCLUDE FILES ======================================================*/ #include /* stdout, stdin, putc(), printf(), getc(), ungetc*() */ /*== MACRO DEFINITIONS (OBJECT-LIKE) ====================================*/ #define FALSE (0) #define TRUE (!FALSE) #define BLANKLINE printf("\n") #define MARGIN (2) /* Left Margin in PrintHeader() */ #define INDENT_SIZE (2) /* Indent relative to */ #define LMARG printc(' ', MARGIN) /* Print Left Margin */ #define INDENT printc(' ', INDENT_SIZE) /* Indent relative to margin */ #define EMPTYLOOP {} #define SIZE (11) /*== EXIT CODE DEFINITIONS ==============================================*/ #define EXIT_PASS (0) #define EXIT_FAIL (1) /*== MACRO DEFINITIONS (FUNCTION-LIKE) ==================================*/ #define PutC(c) (putc((char)(c),stdout)) #define PutD(d) (PutC( (char) ((d)<10)?('0'+(d)):('A'+(d)-10) )) #define GetC() (getc(stdin)) /*=======================================================================*/ /* SUPPORT FUNCTIONS ( functions not called directly by main() ) */ /*=======================================================================*/ int printc(char c, int n) { while ( (n--) && (c == putc(c, stdout)) ); /* EMPTY LOOP */ return n; } int PutV_u(unsigned int n) { unsigned int m; int i, count; /* Set m to the largest power or ten <= n */ for (m = 1; n/m >=10; m*=10) /* EMPTYLOOP */; /* Print out the digits in n one-by-one */ for (count = 0; m > 0; m /= 10) { for(i = 0; n >= m; i++) n -= m; PutD(i); count++; } return count; } int PutV_i(int n) { if (n < 0) { PutC('-'); return 1 + PutV_u(-n); } return PutV_u(n); } int PutS(const char *s) { int i; for(i = 0; '\0' != s[i]; i++) PutC(s[i]); return i; } int isspace(int c) { switch(c) { case ' ': case '\t': case '\n': case '\f': case '\b': case '\v': case '\r': return TRUE; } return FALSE; } int isdigit(int c) { return ( ('0' <= c) && (c <= '9') ); } unsigned int GetV_u(unsigned int *ptr) { unsigned int value; int c; while (isspace(c = GetC())) /* Skip leading whitespace */ EMPTYLOOP; for ( value = 0; isdigit(c); c = GetC() ) /* convert integer */ value = 10*value + (c - '0'); ungetc(c, stdin); /* Return unused character to stream buffer */ if (NULL != ptr) /* write to memory only if valid pointer */ *ptr = value; return value; } int GetV_i(int *ptr) { int value; int c; while (isspace(c = GetC())) /* Skip leading whitespace */ EMPTYLOOP; value = 1; if ( '-' == c ) /* Found a negative sign */ value = -1; else ungetc(c, stdin); /* found something else - push it back */ value *= GetV_u(NULL); if (NULL != ptr) /* write to memory only if valid pointer */ *ptr = value; return value; } /*=======================================================================*/ /* PRIMARY FUNCTIONS ( functions called directly by main() ) */ /*=======================================================================*/ /*== FUNCTION PROTOTYPES (to Document Support Functions) ================*/ int printc(char c, int n); int PutV_i(int n); int PutS(const char *); /*== PRIMARY FUNCTIONS ==================================================*/ void PrintHeader(void) { LMARG; printc('=', (78 - MARGIN)); putc('\n', stdout); LMARG; printf("Course....... %s-%i (%s %i)\n",COURSE,SECTION,TERM,YEAR); LMARG; printf("Programmer... %s (%s)\n", PROGRAMMER, PROG_CODE); LMARG; printf("Assignment... %s (Rev %i)", ASSIGNMENT, REVISION); LMARG; printf("(Source Code in %s)\n", FILENAME); LMARG; printf("Description.. %s\n", TITLE); LMARG; printf(" %s\n", SUBTITLE); LMARG; printc('=', (78 - MARGIN)); putc('\n', stdout); } /*=======================================================================*/ /* MAIN FUNCTION */ /*=======================================================================*/ /*== FUNCTION PROTOTYPES (to Document Primary Functions) ================*/ void PrintHeader(void); int main(void) { unsigned int u; int i; PrintHeader(); BLANKLINE; PutS("Enter an unsigned integer: "); GetV_u(&u); PutS("The value entered was: "); PutV_u(u); PutS("\n"); PutS("Enter signed integer: "); GetV_i(&i); PutS("The value entered was: "); PutV_i(i); PutS("\n"); return EXIT_PASS; } /*=======================================================================*/ /* END OF SOURCE CODE FILE */ /*=======================================================================*/