Thread: Question about reading in strings and integers

  1. #1
    Registered User
    Join Date
    May 2007
    Posts
    3

    Question about reading in strings and integers

    Hi,

    I am reading in strings and integers and I am wondering, am I doing this correctly where I will prevent overflow problems and also not have a bunch of issues with my strings? Since I started messing with C, I have had tremendous problems getting this stuff to work and I think I got it now. I am just wondering how to improve the code I wrote.

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <limits.h>
    
    void read(char *out, const int size);
    int readint(int *out);
    void readdouble(double *out);
    
    int main(void)
    {
            char buffer[17] = {0};
            int i = 0;
            double d = 0;
    
            printf("read string[16]: ");
            read(buffer, 17);
    
            printf("read integer: ");
            if(!readint(&i)) {
                    printf("the number you entered is beyond the range allowable, please try again.");
                    while(!readint(&i)) {
                            printf("the number you entered is beyond the range allowable, please try again.");
                    }
            }
            
            printf("read: %s\t\t%d\n\n", buffer, strlen(buffer));
            printf("readint: %d\n\n", i);
            printf("readint: %5.2f\n\n", d);
            
            system("PAUSE");
            return 0;
    }
    
    /**
            function:       read
            purpose:        reads in a string and assigns it to out. Requires a length of 
                                    string parameter of n+1 (+1 allows for null terminator).
            args:           char *out, const int strlen
            returns:        void
    */
    void read(char *out, const int strlen)
    {
            char ch;
            int char_count = 0;
    
            // Read in the keyboard input until enter is pressed or
            // we exceed the buffer. We will not allow the buffer
            // to overflow by restricting what actually gets written
            // to the pointer.
            ch = getchar();
            *(out)++ = ch;
    
            while(ch != '\n' && ++char_count < strlen-1) {
                    ch = getchar();
                    if(char_count < strlen-1)
                            *(out)++ = ch;
            }
            // Null terminate the pointer so it is a proper string
            *out = '\0';
            // Flush the remaining keyboard input
            fflush(stdin);
    }
    
    /**
            function:       readint
            purpose:        reads an integer and assigns it to the out pointer parameter.
            defines:        INT_LEN
            args:           int *out
            returns:        int (1 success, -1 error)
    */
    #define INT_LEN 100
    int readint(int *out)
    {
            char buf[INT_LEN];              /* buffer for retriving an int */
            char tmpbuf[INT_LEN];   /* temporary buffer for reconverting the int to a string to check for integer overflow */
            int i, tmpbuflen, buflen;
    
            read(buf, INT_LEN);             /* read input from the user as a string */
            i = atoi(buf);                  /* convert the string to an int */
    
            // Reconvert to a string to ensure that the conversion didn't cause an integer overflow
            itoa(i, tmpbuf, 10);
            tmpbuflen = strlen(tmpbuf);
            buflen = strlen(buf) - 1; /* subtract 1 for the null terminator, otherwise comparison will fail */
    
            if(tmpbuflen != buflen || INT_MIN < i && i > INT_MAX) // check if they are the same and within signed integer ranges
                    return 0;
            
            *out = i;
            return 1;
    }

  2. #2
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    I have some problems with your readint() function. For starters, atoi() is generally considered obsolete and it is recommended you use strtol() instead, which checks for an int overflow:

    If the string has valid syntax for an integer but the value is not representable because of overflow, strtol returns either LONG_MAX or LONG_MIN (see Range of Type), as appropriate for the sign of the value. It also sets errno to ERANGE to indicate there was overflow.
    Also, your own method for doing this is not good. Converting back into a string and comparing the lengths will not work if, for example, the value "wraps around"* creating a negative number, because including the minus sign the string could be the same length. For example, 4147483648 will become -147483648 if converted into a (32 bit) signed int. As strings, both those have 10 characters. You could, however, reliably use strcmp() here, in which case you would not have to check anything else (if you insist on using atoi).

    Furthermore, testing an int to see if it exceeds the minimum or maximum value an int can hold is oxymoronic. It is impossible for an int to return a value outside this range -- that is what the range indicates. Get it? Because of this and the way you have arranged your conditions here,

    Code:
    if(tmpbuflen != buflen || INT_MIN < i && i > INT_MAX)
    This function will NEVER return an error, because the final condition says "AND i is greater than INT_MAX". That is impossible, so the if will always fail. I think perhaps you meant && to be ||, but in any case as demonstrated this is an irrelevant test.

    *which is what an int overflow does (altho in the case of a string conversion it may not literally be a wrap around the effect is similar). An int overflow is not like a buffer overflow, it does not overwrite the bounds of the int, it just results in an arithmetically incorrect result (or in this case an incorrect string converstion).
    Last edited by MK27; 11-20-2009 at 04:21 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  3. #3
    Registered User
    Join Date
    Jan 2009
    Posts
    21
    Several things:

    (linux* - I see you are win with your pause)

    fflush(FILE*) flushes the write buffer - it writes the buffer to disk or whatever the driver wants. It does not clear the stdin. Look into terminals, term.
    Thins explains: Cprogramming.com FAQ > Why fflush(stdin) is wrong

    your
    Code:
    if(!readint(&i)) {
      printf("the number you entered is beyond the range allowable, please try again.");
      while(!readint(&i)) {
        printf("the number you entered is beyond the range allowable, please try again.");
      }
    }
    Why the if and while?
    Just the while statement will handle that in the same way.

    Similarly, with your reading in date in read(). In your read function you read in the '\n'. Also, if you have any interest in testing for EOF you should use int ch because EOF is -1 but for stdin that is really n/a.
    Code:
    while (ch != '\n' && ++char_count < strlen-1) {  /* this tests previously read in char */
      ch = getchar ();                /* this gets char */
      if (char_count < strlen-1)
        *(out)++ = ch;               /* this assigns char */
    }
    while ((ch = getchar()) != '\n' && ++char_count < strlen-1)
      *(out)++ = ch;                 /* may do better */
    In glibc there is no such thing as itoa; however in yours there may be - I don't know - I have nothing to say there.

    You don't use d except an initialization and printing it out - if you want to be proper, d is a double and f is float so it should be %5.2lf but printf manages.

    As far as your avoidance of overruns, I see them well prevented.

  4. #4
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    I didn't even even notice the fflush(stdin)! That is undefined by the C standard and should never be used.

    You may want to read this too:
    STDIN pitfalls
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  5. #5
    Registered User
    Join Date
    May 2007
    Posts
    3
    @MK27
    Thanks for your response. After researching strtol I see the benefit of using it. Also I get to accomplish what I set out to do, actually verify what is being read in is in an int range by reading it to a long and then use INT_MIN and INT_MAX to verify it's a proper integer. It also gave me an opportunity to learn about errno.h (which I had no clue it existed). Also thanks for the link about fflush, I had no idea it was for flushing output only. I have a bunch of tutorials that use it rather haphazardly. Guess it's back to K&R for me.

    @seaking1
    Thanks for pointing out my lack of optimization. I was being a little lazy, I was doing this when I was supposed to be working on a JavaScript GUI library. I love C! It's so much fun, well so far anyways. Also thanks for the great link.

    One other question, if a name or something should be a particular size, is my way of handling this consistent with what you would see in other C programs or should I flag it bad if it exceeds expectations?

    Here is the code update, any more suggestions?
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <limits.h>
    #include <errno.h>
    
    void read(char *out, const int size);
    int readint(int *out);
    
    int main(void)
    {
    	char buffer[17] = {0};
    	int i = 0;
    	//double d = 0;
    	printf("read string[16]: ");
    	read(buffer, 17);
    	
    	printf("read integer: ");
    	while(!readint(&i)) {
    		printf("the number you entered is beyond the range allowable, please try again.");
    	}
    
    	//printf("read double: ");
    	//readdouble(&d);
    	
    	printf("read: %s\t\t%d\n\n", buffer, strlen(buffer));
    	printf("readint: %d\n\n", i);
    	//printf("readint: %5.2f\n\n", d);
    	
    	system("PAUSE");
    	return 0;
    }
    
    /**
    	function:	read
    	purpose:	reads in a string and assigns it to out. Requires a length of 
    				string parameter of n-1 (-1 allows for null terminator).
    	args:		char *out, const int strlen
    	returns:	void
    */
    void read(char *out, const int strlen)
    {
    	char ch;
    	int char_count = 0;
    
    	// Read in the keyboard input until enter is pressed or
    	// we exceed the buffer. We will not allow the buffer
    	// to overflow by restricting what actually gets written
    	// to the pointer.
    	ch = getchar();
    	*(out)++ = ch;
    
    	while((ch = getchar()) != '\n' && ch != EOF)
    		if(++char_count < strlen-1) // If we are outside of the range of chars to read, read the rest of characters in stdin
    			*(out)++ = ch;
    
    	// Null terminate the pointer so it is a proper string
    	*out = '\0';
    }
    
    /**
    	function:	readint
    	purpose:	reads an integer and assigns it to the out pointer parameter.
    	defines:	INT_LEN
    	args:		int *out
    	returns:	int (1 success, 0 error)
    */
    #define	INT_LEN	100
    int readint(int *out)
    {
    	long int val;
    	char str[INT_LEN];		/* buffer for retriving an int */
    	char **endptr = {'\0'};	/* end ptr for strtol */
    
    	read(str, INT_LEN);		/* read input from the user as a string */
    
    	/* convert the string to a long and ensure it didn't
    	   wrap/exceed the bounds of an integer or the conversion 
    	   could not be performed (such as reading a string) */
    	val = strtol(str, endptr, 0);
    
    	if(errno == ERANGE || val == 0 && INT_MIN < val > INT_MAX) {
    		errno = 0;	/* reset the error code */
    		return 0;
    	}
    
    	*out = (int)val;
    	return 1;
    }
    Last edited by sgalland; 11-21-2009 at 11:24 PM.

  6. #6
    Registered User
    Join Date
    May 2007
    Posts
    3
    Another question, what books do you guys recommend for C? I found tutorials that seem to teach bad principles (fflush(stdin)). The only reason I got this far is that I knew how pointers worked from C++, but I am relatively new to ANSI C (well coming back to it after trying/failing long time ago).

  7. #7
    DESTINY BEN10's Avatar
    Join Date
    Jul 2008
    Location
    in front of my computer
    Posts
    804
    HOPE YOU UNDERSTAND.......

    By associating with wise people you will become wise yourself
    It's fine to celebrate success but it is more important to heed the lessons of failure
    We've got to put a lot of money into changing behavior


    PC specifications- 512MB RAM, Windows XP sp3, 2.79 GHz pentium D.
    IDE- Microsoft Visual Studio 2008 Express Edition

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Reading Lines from File Issue (integers and character strings)
    By kbfirebreather in forum C Programming
    Replies: 4
    Last Post: 10-17-2009, 02:02 PM
  2. Converting Integers to Strings
    By momo97 in forum C++ Programming
    Replies: 5
    Last Post: 03-04-2009, 01:36 AM
  3. Replies: 9
    Last Post: 03-17-2006, 12:44 PM
  4. fread - reading strings and integers.
    By Vber in forum C Programming
    Replies: 1
    Last Post: 11-17-2002, 04:08 PM
  5. reading string and integers from a text file
    By Gator in forum C Programming
    Replies: 2
    Last Post: 11-25-2001, 02:17 PM