Author |
Message |
tritra #1 / 14
|
 String converter
I like to write the function that get the string from the standard input the check whether the string is integer and accept it, also accept the space between the integer. For example : input: 123 432 422 How can i convert the string to integer. I am using atoi however it only convert the first 123 and leave the rest. When i test the function it show an error "assignment make pointers from integer without a cast" Can someone help me fix this problem. int read(char *p) { char message[90]; int check; p=fgets(message,90,stdin); if (isdigit(*p)||isspace(*p)) { check= atoi(p); return check; } Quote: }
|
Wed, 09 Feb 2005 15:03:59 GMT |
|
 |
Malcol #2 / 14
|
 String converter
Quote:
> I like to write the function that get the string from the standard input the > check whether the string is integer and accept it, also accept the space > between the integer. > For example : > input: 123 432 422 > How can i convert the string to integer. > I am using atoi however it only convert the first 123 and leave the rest. > When i test the function it show an error > "assignment make pointers from integer without a cast" > Can someone help me fix this problem. > int read(char *p) > { > char message[90]; > int check; > p=fgets(message,90,stdin); > if (isdigit(*p)||isspace(*p)) > { > check= atoi(p); > return check; > } > }
char *skipdecimal(const char *str) { while(isspace(*str)) str++; while(isdigit(*str)) str++; return str; Quote: }
|
Wed, 09 Feb 2005 17:44:15 GMT |
|
 |
Jens.Toerr.. #3 / 14
|
 String converter
Quote:
> I like to write the function that get the string from the standard input the > check whether the string is integer and accept it, also accept the space > between the integer. > For example : > input: 123 432 422 > How can i convert the string to integer. > I am using atoi however it only convert the first 123 and leave the rest. > When i test the function it show an error > "assignment make pointers from integer without a cast" > Can someone help me fix this problem. > int read(char *p) > { > char message[90]; > int check; > p=fgets(message,90,stdin);
What is using 'p' good for? A simple local char pointer is all you need (and the pointer you pass to the function will, luckily for you, not be changed, anyway). Quote: > if (isdigit(*p)||isspace(*p)) > { > check= atoi(p); > return check; > }
Missing return value if the first character is neither a digit nor a white space. Quote: > }
There are basically two strategies, both involving that you loop over the string yourself. If you insist on using atoi() you will have to check the contents of message one by one and for characters not being digits (or a leading minus sign) you have to copy the later contents of the array over the none-digit character(s), so that you end up with a string only consisting of digits (except leading white space and a minus sign). Probably simpler is just looping over the string, skipping white space and calculating the result manually, perhaps like this (take care, code is untested): int read( void ) { char message[ 90 ]; char *cur; sign = 1; int val = 0; fgets( message, 90, stdin ); /* Skip leading white space part of string and catch minus sign */ for ( cur = message; *cur != '\0' && ! isdigit( *cur ) ; cur++ ) { if ( *cur == '-' ) { sign = -1; break; } if ( ! isspace( *cur ) ) /* invalid characer ? */ exit( EXIT_FAILURE ); } /* Loop over rest of string, calculating the resulting number and skipping white space */ for ( ; *cur != '\0'; cur++ ) if ( isdigit( *cur ) ) { val = val * 10 + ( *cur - '0' ); if ( val < 0 ) /* integer overflow ? */ exit( EXIT_FAILURE ); } else if ( ! isspace( *cur ) ) /* invalid characer ? */ exit( EXIT_FAILURE ); return sign * val; Quote: }
As you see, you also have to think about what to do in cases where the string either contains non-white-space character that aren't digits or where the number is too large to be represented by an integer. What's also not checked here is if there were any digits at all in the string, in this case 0 is returned. I don't know if this is what you want, other- wise you need an additional flag that gets set when an digit is found. At the end you check if the flag is set, if not take the appropriate action. Regards, Jens -- _ _____ _____
_ | | | | | | | |_| | | | | | http://www.physik.fu-berlin.de/~toerring \___/ens|_|homs|_|oerring
|
Wed, 09 Feb 2005 18:15:21 GMT |
|
 |
Eric G. Mille #4 / 14
|
 String converter
Quote:
> I like to write the function that get the string from the standard input the > check whether the string is integer and accept it, also accept the space > between the integer.
Already I'm confused by your requirements. Perhaps you want more than one function, the first of which just verifies the input contains only digits and space characters? Quote: > For example : > input: 123 432 422 > How can i convert the string to integer. > I am using atoi however it only convert the first 123 and leave the rest. > When i test the function it show an error > "assignment make pointers from integer without a cast"
This error isn't from the function below, but you'll want to figure out where exactly this is occurring, since it is a bug. Quote: > Can someone help me fix this problem. > int read(char *p)
This is perhaps a bad function name, as "read" is often a system function. Quote: > { > char message[90]; > int check; > p=fgets(message,90,stdin);
Perhaps you don't realize the caller's char * passed to the function will never be updated (but you wouldn't want to return a pointer to memory local to the function anyway). Quote: > if (isdigit(*p)||isspace(*p))
You don't know that p != NULL, so *p could crash your program. Also, this only checks the first character of the buffer, but you apparently want to check them all. Quote: > { > check= atoi(p);
atoi() is not a great function since you can't catch errors with it. You're problem would be better addressed using strtol(). Quote: You're probably better off just trying to parse out the string into a dynamic array of ints, and failing on error: #include <stdlib.h> #include <limits.h> #include <errno.h> #include <ctype.h> /* * parse_ints: splits "line" into "nums", returning the count. * "nums" is a dynamically allocated int *, and must be freed * by the caller if parse_ints returns a number greater than * zero. * Returns -1 on error. */ int parse_ints (char *line, int **nums) { int count = 0; int errors = 0; long number; int *array = NULL, *iptr; char *cptr, *endptr; cptr = line; while (cptr != NULL && *cptr != '\0' && !errors) { errno = 0; number = strtol (cptr, &endptr, 10); if (cptr == endptr || errno == ERANGE) errors++; if (number < INT_MIN || number > INT_MAX) errors++; if (!errors) { iptr = realloc (array, sizeof *iptr * (count + 1)); if (iptr != NULL) { array = iptr; array[count++] = (int) number; } else { errors++; } } cptr = endptr; while (isspace(*cptr)) cptr++; } if (errors) { free (array); count = -1; } else { *nums = array; } return count; Quote: }
|
Wed, 09 Feb 2005 19:21:02 GMT |
|
 |
Mark McIntyr #5 / 14
|
 String converter
On Sat, 24 Aug 2002 17:03:59 +1000, in comp.lang.c , "tritran" Quote:
>I like to write the function that get the string from the standard input the >check whether the string is integer and accept it, also accept the space >between the integer. >For example : >input: 123 432 422 >How can i convert the string to integer.
do you mean that you want to get the value 123432422? Or do you want to get each integer in turn ie 3 values? If the latter, use fgets and then strtok or something similar. If the former, copy each char into a new string provided its not blank. Be careful of overflowing your chosen integer type. -- Mark McIntyre CLC FAQ <http://www.eskimo.com/~scs/C-faq/top.html> CLC readme: <http://www.angelfire.com/ms3/bchambless0/welcome_to_clc.html>
|
Wed, 09 Feb 2005 23:30:30 GMT |
|
 |
tritra #6 / 14
|
 String converter
Quote:
> > I like to write the function that get the string from the standard input the > > check whether the string is integer and accept it, also accept the space > > between the integer. > Already I'm confused by your requirements. Perhaps you want more than one > function, the first of which just verifies the input contains only digits > and space characters? > > For example : > > input: 123 432 422 > > How can i convert the string to integer. > > I am using atoi however it only convert the first 123 and leave the rest. > > When i test the function it show an error > > "assignment make pointers from integer without a cast" > This error isn't from the function below, but you'll want to figure out > where exactly this is occurring, since it is a bug. > > Can someone help me fix this problem. > > int read(char *p) > This is perhaps a bad function name, as "read" is often a system function. > > { > > char message[90]; > > int check; > > p=fgets(message,90,stdin); > Perhaps you don't realize the caller's char * passed to the function > will never be updated (but you wouldn't want to return a pointer to > memory local to the function anyway). > > if (isdigit(*p)||isspace(*p)) > You don't know that p != NULL, so *p could crash your program. Also, this > only checks the first character of the buffer, but you apparently want > to check them all. > > { > > check= atoi(p); > atoi() is not a great function since you can't catch errors with it. > You're problem would be better addressed using strtol(). > > return check; > > } > > } > You're probably better off just trying to parse out the > string into a dynamic array of ints, and failing on error:
Thank you very much for answer me this problem however i am a newbie and i haven't study much about pointers and your way seem difficult for me to catch up. Could you please provide the simple one or explain to me step by step. Quote: > #include <stdlib.h> > #include <limits.h> > #include <errno.h> > #include <ctype.h> > /* > * parse_ints: splits "line" into "nums", returning the count. > * "nums" is a dynamically allocated int *, and must be freed > * by the caller if parse_ints returns a number greater than > * zero. > * Returns -1 on error. > */ > int parse_ints (char *line, int **nums) {
What's "**" use for? Why we need this one? Quote: > int count = 0; > int errors = 0; > long number; > int *array = NULL, *iptr; > char *cptr, *endptr; > cptr = line; > while (cptr != NULL && *cptr != '\0' && !errors) { > errno = 0; > number = strtol (cptr, &endptr, 10); > if (cptr == endptr || errno == ERANGE) errors++; > if (number < INT_MIN || number > INT_MAX) errors++;
you haven't declare the varibale for INT_MIN and INT_MAX are they from limits.h if (!errors) { Quote: > iptr = realloc (array, sizeof *iptr * (count + 1));
I haven't study up to realloc so that any other way to do without having a problem with memory allocation Quote: > if (iptr != NULL) { > array = iptr; > array[count++] = (int) number; > } > else { > errors++; > } > } > cptr = endptr; > while (isspace(*cptr)) > cptr++; > } > if (errors) { > free (array);
Is free() function is use to free the memory when you using realloc, could you explain why we need to free the memory after using the calloc or realloc I read from "the book on C " but hardly to understand why and sometime we have to use sizeof(something) but above you are using sizeof *iptr is it the right syntax Quote: > count = -1; > } > else { > *nums = array; > } > return count; > }
|
Thu, 10 Feb 2005 12:25:58 GMT |
|
 |
tritra #7 / 14
|
 String converter
Thank you so much but i would like to know that how we use flag to detect the errors in this program and if the input like 123 456 it will return the error because it's not an integer how can i do that and why you need to catch the '-' sign caused also want to input the negative number .
Quote:
> > I like to write the function that get the string from the standard input the > > check whether the string is integer and accept it, also accept the space > > between the integer. > > For example : > > input: 123 432 422 > > How can i convert the string to integer. > > I am using atoi however it only convert the first 123 and leave the rest. > > When i test the function it show an error > > "assignment make pointers from integer without a cast" > > Can someone help me fix this problem. > > int read(char *p) > > { > > char message[90]; > > int check; > > p=fgets(message,90,stdin); > What is using 'p' good for? A simple local char pointer is all you need > (and the pointer you pass to the function will, luckily for you, not be > changed, anyway). > > if (isdigit(*p)||isspace(*p)) > > { > > check= atoi(p); > > return check; > > } > Missing return value if the first character is neither a digit nor a > white space. > > } > There are basically two strategies, both involving that you loop over > the string yourself. If you insist on using atoi() you will have to > check the contents of message one by one and for characters not being > digits (or a leading minus sign) you have to copy the later contents > of the array over the none-digit character(s), so that you end up with > a string only consisting of digits (except leading white space and a > minus sign). > Probably simpler is just looping over the string, skipping white space > and calculating the result manually, perhaps like this (take care, > code is untested): > int read( void ) > { > char message[ 90 ]; > char *cur; > sign = 1; > int val = 0; > fgets( message, 90, stdin ); > /* Skip leading white space part of string and catch minus sign */ > for ( cur = message; *cur != '\0' && ! isdigit( *cur ) ; cur++ ) > { > if ( *cur == '-' ) > { > sign = -1; > break; > } > if ( ! isspace( *cur ) ) /* invalid characer ? */ > exit( EXIT_FAILURE ); > } > /* Loop over rest of string, calculating the resulting number and > skipping white space */ > for ( ; *cur != '\0'; cur++ ) > if ( isdigit( *cur ) ) > { > val = val * 10 + ( *cur - '0' ); > if ( val < 0 ) /* integer overflow ? */ > exit( EXIT_FAILURE ); > } > else if ( ! isspace( *cur ) ) /* invalid characer ? */ > exit( EXIT_FAILURE ); > return sign * val; > } > As you see, you also have to think about what to do in cases where the > string either contains non-white-space character that aren't digits > or where the number is too large to be represented by an integer. What's > also not checked here is if there were any digits at all in the string, > in this case 0 is returned. I don't know if this is what you want, other- > wise you need an additional flag that gets set when an digit is found. > At the end you check if the flag is set, if not take the appropriate > action. > Regards, Jens > -- > _ _____ _____
> _ | | | | | | > | |_| | | | | | http://www.physik.fu-berlin.de/~toerring > \___/ens|_|homs|_|oerring
|
Thu, 10 Feb 2005 21:46:30 GMT |
|
 |
tritra #8 / 14
|
 String converter
I look at your program and found that there is the problem when i enter more than 10 integer number for example input: 1234567891 output: 1234567891 however input: 12345678912 output: 1494771484 if more than 12 character it will not tested or appear the output. Could you please explain to me how come the output like that i tried to debug several time but it's not correct, may be i am not clearly understand your program .Correct me if i am wrong: /* You trying to read the string from the first character to the '\0' and check each of them is number or not */ for ( ; *cur != '\0'; cur++ ) if ( isdigit( *cur ) ) { /*i am not clearly understand why we have to multiply by 10 here, is it the way we check the number */ val = val * 10 + ( *cur - '0' ); if ( val < 0 ) /* integer overflow ? */ exit( EXIT_FAILURE ); } else if ( ! isspace( *cur ) ) /* invalid characer ? */ exit( EXIT_FAILURE );. Another problem is that the result will not treat the input string as an integer caused when i tested with only 1 number such as choice the option from menu and found that it's return the value as the string or different value for example enter an integer 5 it's print out the different number but not 5. Also can i use atoi to convert the string into the integer, how can i implement your program with atoi.
Quote:
> > I like to write the function that get the string from the standard input the > > check whether the string is integer and accept it, also accept the space > > between the integer. > > For example : > > input: 123 432 422 > > How can i convert the string to integer. > > I am using atoi however it only convert the first 123 and leave the rest. > > When i test the function it show an error > > "assignment make pointers from integer without a cast" > > Can someone help me fix this problem. > > int read(char *p) > > { > > char message[90]; > > int check; > > p=fgets(message,90,stdin); > What is using 'p' good for? A simple local char pointer is all you need > (and the pointer you pass to the function will, luckily for you, not be > changed, anyway). > > if (isdigit(*p)||isspace(*p)) > > { > > check= atoi(p); > > return check; > > } > Missing return value if the first character is neither a digit nor a > white space. > > } > There are basically two strategies, both involving that you loop over > the string yourself. If you insist on using atoi() you will have to > check the contents of message one by one and for characters not being > digits (or a leading minus sign) you have to copy the later contents > of the array over the none-digit character(s), so that you end up with > a string only consisting of digits (except leading white space and a > minus sign). > Probably simpler is just looping over the string, skipping white space > and calculating the result manually, perhaps like this (take care, > code is untested): > int read( void ) > { > char message[ 90 ]; > char *cur; > sign = 1; > int val = 0; > fgets( message, 90, stdin ); > /* Skip leading white space part of string and catch minus sign */ > for ( cur = message; *cur != '\0' && ! isdigit( *cur ) ; cur++ ) > { > if ( *cur == '-' ) > { > sign = -1; > break; > } > if ( ! isspace( *cur ) ) /* invalid characer ? */ > exit( EXIT_FAILURE ); > } > /* Loop over rest of string, calculating the resulting number and > skipping white space */ > for ( ; *cur != '\0'; cur++ ) > if ( isdigit( *cur ) ) > { > val = val * 10 + ( *cur - '0' ); > if ( val < 0 ) /* integer overflow ? */ > exit( EXIT_FAILURE ); > } > else if ( ! isspace( *cur ) ) /* invalid characer ? */ > exit( EXIT_FAILURE ); > return sign * val; > } > As you see, you also have to think about what to do in cases where the > string either contains non-white-space character that aren't digits > or where the number is too large to be represented by an integer. What's > also not checked here is if there were any digits at all in the string, > in this case 0 is returned. I don't know if this is what you want, other- > wise you need an additional flag that gets set when an digit is found. > At the end you check if the flag is set, if not take the appropriate > action. > Regards, Jens > -- > _ _____ _____
> _ | | | | | | > | |_| | | | | | http://www.physik.fu-berlin.de/~toerring > \___/ens|_|homs|_|oerring
|
Thu, 10 Feb 2005 23:12:42 GMT |
|
 |
Jens.Toerr.. #9 / 14
|
 String converter
Quote:
> Thank you so much but i would like to know that how we use flag to detect > the errors in this program and if the input like 123 456 it will return > the error because it's not an integer how can i do that and why you need to > catch the '-' sign caused also want to input the negative number .
As far as I understood you you want to get the number 12345 from an input string like " 12 3 45 ", if this isn't the case you can forget about all of the rest. I now included the flag ('found_digit') to figure out if there were any digits at all in the input, see below. And, since you were writing about integers you obviously have to take into account negative numbers and thus minus signs. And, finally, since you were only testing for white- space characters I assume that other non-digits are to be treated as an error in the input. Quote: > I look at your program and found that there is the problem when i enter more > than 10 integer number for example > input: 1234567891 > output: 1234567891 > however > input: 12345678912 > output: 1494771484 > if more than 12 character it will not tested or appear the output. Could you > please explain to me how come the output like that i tried to debug several
Integers can only represent numbers up to certain limits. The standard only requires that integers in the range between -32767 and 32767 must be representable. Your implementation seems to allow larger value, from the look of it you have 4 byte integers, resulting into a representable range between 2147483647 and -2147483647 (or similar, to find out look up the INT_MAX and INT_MIN values in linits.h). Thus you simply can't store numbers like 12345678912, they are too large. It's possible (but not required) that long integers can store larger numbers, too find out check the LONG_MAX and LONG_MIN values in limits.h. Quote: > time but it's not correct, may be i am not clearly understand your program > .Correct me if i am wrong: > /* You trying to read the string from the first character > to the '\0' and check each of them is number or not */ > for ( ; *cur != '\0'; cur++ ) > if ( isdigit( *cur ) ) > { > /*i am not clearly understand why we have to multiply by 10 > here, is it the way we check the number */ > val = val * 10 + ( *cur - '0' ); > if ( val < 0 ) /* integer overflow ? */ > exit( EXIT_FAILURE ); > }
No, the multiplication by 10 is simply how you calculate the number. Let's assume you have the string "123". 'val' is 0 at first, so multiplication with 10 leaves it 0. Now you add the first number from the string, 1 (but since you only have the character representation of the number, the character '1', you must subtract the char representation of '0' to get the *number* 1). 'val' is now 1. The next time through the loop, 1 is multiplicated by ten, giving you 10. Now 2 is added, so 'val' is now 12. The next and last time through the loop, the multiplication gives 120, then you add 3 and thus end up with val beeing 123, the number you were looking for. The check if 'val' is negative is there to find out if the number got larger than the maximum value you can represent with an integer value. In this case you get a wrap-around, i.e. too large a number gets truncated and you end up with a *negative* number. Quote: > else if ( ! isspace( *cur ) ) /* invalid characer ? */ > exit( EXIT_FAILURE );. > Another problem is that the result will not treat the input string as an > integer caused when i tested > with only 1 number such as choice the option from menu and found that it's > return the value as the string or > different value for example enter an integer 5 it's print out the different > number but not 5. Also can i use atoi
Sorry, I don't understand what you mean here. Could you please try to rephrase it a bit? Quote: > to convert the string into the integer, how can i implement your program > with atoi.
I also added a version where you can use atoi(). Unfortunately, it's even more complicated because of all the necessary copying within the string. Also finding out if the number in the string isn't too large is a bit more tricky. I hope the comments give you an idea how it works. Regards, Jens PS: Please stop top-posting: put your comments etc. below what you quote of other peoples messages. This way it's much easier to follow what you refer to. And please also remove everything not strictly necessary from what you quote, no-one likes to have to browse through hundreds of lines of useless text just to find the one interesting point. ----------------------8<--------------------------------------------------- #include <stdio.h> #include <ctype.h> #include <string.h> #include <stdlib.h> #include <limits.h> int read_int_v1( const char *message ); int read_int_v2( const char *message ); int main( void ) { char message[ 90 ]; if ( fgets( message, 90, stdin ) == NULL ) { fprintf( stderr, "No input found\n" ); exit( EXIT_FAILURE ); } printf( "Version 1: You entered %d\n", read_int_v1( message ) ); printf( "Version 2: You entered %d\n", read_int_v2( message ) ); return EXIT_SUCCESS; Quote: }
int read_int_v1( const char *message ) { const char *cur; int sign = 1; int val = 0; int found_digit = 0; /* Skip leading white space at start of string and catch minus sign */ for ( cur = message; *cur != '\0' && ! isdigit( *cur ) ; cur++ ) { if ( *cur == '-' ) { sign = -1; cur++; break; } if ( ! isspace( *cur ) ) /* invalid characer ? */ { fprintf( stderr, "Invalid character: '%c'\n", *cur ); exit( EXIT_FAILURE ); } } /* Loop over rest of string, calculating the resulting number and skipping white space */ for ( ; *cur != '\0'; cur++ ) if ( isdigit( *cur ) ) { found_digit = 1; val = val * 10 + ( *cur - '0' ); if ( val < 0 ) /* integer overflow ? */ { fprintf( stderr, "Integer overflow\n" ); exit( EXIT_FAILURE ); } } else if ( ! isspace( *cur ) ) /* invalid characer ? */ { fprintf( stderr, "Invalid character: '%c'\n", *cur ); exit( EXIT_FAILURE ); } /* Check if there were any digits at all in the input */ if ( ! found_digit ) { fprintf( stderr, "No digits found in input\n" ); exit( EXIT_FAILURE ); } return sign * val; Quote: }
int read_int_v2( const char *message ) { char str[ 90 ]; char buffer[ 90 ]; char *p0, *p1, *p2, *p_end; int res; /* Make sure to only modify a copy of the input string, so the function can also be applied to strings we're not allowed to modify. */ strcpy( str, message ); /* Start off by looking for the first character that's either a digit or a minus sign (bail out when there's an error in the input). */ for ( p0 = str; *p0 != '\0' && isspace( *p0 ) && *p0 != '-'; p0++ ) /* empty */ ; if ( *p0 == '\0' ) { fprintf( stderr, "No numbers found in input\n" ); exit( EXIT_FAILURE ); } if ( ! isdigit( *p0 ) && *p0 != '-' ) { fprintf( stderr, "Invalid character: '%c'\n", *p0 ); exit( EXIT_FAILURE ); } /* Also calculate a pointer to the very end of the input string, i.e. the trailing '\0'. */ for ( p_end = p0 + 1; *p_end != '\0'; p_end++ ) /* empty */ ; /* p0 points now to the first digit or a minus sign. Now, in the remaining string, look for sets of white-space characters followed by a digit. In this case we simply copy everything following this region to the place where the first white-space character was found. */ for ( p1 = p0 + 1; *p1 != '\0'; p1++ ) { if ( isdigit( *p1 ) ) continue; if ( ! isspace( *p1 ) ) { fprintf( stderr, "Invalid character: '%c'\n", *p1 ); exit( EXIT_FAILURE ); } /* p1 now points to a white-space character. Loop over the following characters, using pointer p2, until a digit is found (or an error in the input is detected). */ for ( p2 = p1 + 1; p2 != '\0' && isspace( *p2 ); p2++ ) /* empty */ ; if ( *p2 == '\0' ) /* string ended with white-space ? */ { *p1 = '\0'; break; } if ( ! isdigit( *p2 ) ) { fprintf( stderr, "Invalid character: '%c'\n", *p2 ); exit( EXIT_FAILURE ); } /* p2 now points to a digit and we move everything from p2 to the end of the string (including the trailing '\0') to where p1 points. Since the string is now shorter we also have to correct the end pointer, p_end. */ memmove( p1, p2, p_end - p2 + 1 ); p_end -= p2 - p1; } /* Now check that we didn't got just a minus sign and nothing else. */ if ( *p0 == '-' && *( p0 + 1 ) == '\0' ) { fprintf( stderr, "Only found a minus sign\n" ); exit( EXIT_FAILURE ); } /* Before we can return the result of applying atoi() to p0 we must check that the number we got isn't too large for an integer. We do this by comparing the cleaned-up string with the one we get when we print the return value of atoi() into a string. If both are identical everything's ok, otherwise the original number was too large for an integer. */ res = atoi( p0 ); sprintf( buffer, "%d", res ); if ( strcmp( p0, buffer ) ) /* strings differ ? */
... read more »
|
Fri, 11 Feb 2005 21:44:43 GMT |
|
 |
Eric G. Mille #10 / 14
|
 String converter
Quote:
>> You're probably better off just trying to parse out the >> string into a dynamic array of ints, and failing on error: > Thank you very much for answer me this problem however i am a newbie and i > haven't study much about pointers and your way seem difficult for me to > catch up. Could you please provide the simple one or explain to me step by > step.
Well, one solution is my limit ;-) But, I'll try to explain... Quote: >> #include <stdlib.h> >> #include <limits.h> >> #include <errno.h> >> #include <ctype.h> >> /* >> * parse_ints: splits "line" into "nums", returning the count. >> * "nums" is a dynamically allocated int *, and must be freed >> * by the caller if parse_ints returns a number greater than >> * zero. >> * Returns -1 on error. >> */ >> int parse_ints (char *line, int **nums) { > What's "**" use for? Why we need this one?
This function "returns" two things. Well, you can only return one thing from a C function, but the caller will need not only the array of ints, but also how many there are. So the function returns the "count", the normal way, and returns the dynamically allocated array via a pointer to pointer supplied by the caller. All C functions receive their arguments by value. So, in order to "return" the array, the caller sends a pointer to the pointer that will hold the result. Maybe, a demonstration: Calling function: int *nums = NULL; int count = parse_ints (instr, &nums); ... Taking the address of a variable yields a pointer to that variable. Now, parse_ints can change its view of "nums" all it wants and the caller will never see the changes. However, if parse_ints changes "*nums", then the caller will see the changes. The caller originally has: nums -> NULL The caller calls parse_ints with "&nums" and parse_ints sees: nums -> <some address> -> NULL (same NULL from caller). Then parse_ints updates *nums: int * array = ... ; /* create the array somehow ... */ *nums = array; /* Sets the callers "nums" to array */ Well, that probably didn't make any sense... Sorry ;-( Quote: >> int count = 0; >> int errors = 0; >> long number; >> int *array = NULL, *iptr; >> char *cptr, *endptr; >> cptr = line; >> while (cptr != NULL && *cptr != '\0' && !errors) { >> errno = 0; >> number = strtol (cptr, &endptr, 10); >> if (cptr == endptr || errno == ERANGE) errors++; >> if (number < INT_MIN || number > INT_MAX) errors++; > you haven't declare the varibale for INT_MIN and INT_MAX are they from > limits.h
limits.h is a standard header that includes many useful constants, like INT_MIN, CHAR_BIT, etc... Quote: > if (!errors) { >> iptr = realloc (array, sizeof *iptr * (count + 1)); > I haven't study up to realloc so that any other way to do without having a > problem with memory allocation
An alternative way might be to set a limit on the number of ints in a "line" of input, then you could use a static declaration. Or possibly just use a static quantity, but make it possible to process each input line partially. I gave a general solution since I didn't have enough info to judge how the function would be used. Quote: >> if (iptr != NULL) { >> array = iptr; >> array[count++] = (int) number; >> } >> else { >> errors++; >> } >> } >> cptr = endptr; >> while (isspace(*cptr)) >> cptr++; >> } >> if (errors) { >> free (array); > Is free() function is use to free the memory when you using realloc, could > you explain why we need to free the memory after using the calloc or realloc > I read from "the book on C " but hardly to understand why and sometime we > have to use sizeof(something) but above you are using sizeof *iptr is it the > right syntax
malloc, calloc and realloc allocate dynamic memory from "somewhere". This memory is not released back to the operating system unless it is explicitly free'd or the program exits (on most hosted OS's). So, every time you use these functions, you grab more system memory for an indefinite period. If you don't free memory when you no longer are using it and lose all pointers to the memory location, you create a memory leak (i.e. you have no way to free or reuse the memory, but the OS still thinks you're using it). Higher level languages tend to provide methods to automatically free memory when it is no longer used, but in C you must take care of memory management yourself. Note: The above free() call is only called if an error occurred. Otherwise, the function assumes that the caller will free the memory when they're done with it. For instance: char buff[80]; int *nums = NULL; int count; while (NULL != fgets (buff, sizeof buff, stdin)) { count = parse_ints (buff, &nums); if (count > 0) { /* do something with "nums" */ free (nums); nums = NULL; } else if (count < 0) { /* report the error */ } else { /* report empty input? */ } }
|
Sat, 12 Feb 2005 11:44:01 GMT |
|
 |
David Thompso #11 / 14
|
 String converter
... Quote: > Probably simpler is just looping over the string, skipping white space > and calculating the result manually, perhaps like this (take care, > code is untested): ... > val = val * 10 + ( *cur - '0' ); > if ( val < 0 ) /* integer overflow ? */ > exit( EXIT_FAILURE );
This isn't safe. First off, overflow is Undefined Behavior, and can trap before ever getting to your test. But even on a machine that silently wraps, as many do nowadays, this doesn't catch about half of all overflows. Consider, on a 16-bit system (for simplicity), 65540. After four digits val==6554 and *cur=='0'. val*10 wraps to 4, plus 0 is 4, is not < 0 but has overflowed. -- - David.Thompson 1 now at worldnet.att.net
|
Mon, 14 Feb 2005 04:17:48 GMT |
|
 |
Jens.Toerr.. #12 / 14
|
 String converter
Quote:
> ... >> Probably simpler is just looping over the string, skipping white space >> and calculating the result manually, perhaps like this (take care, >> code is untested): > ... >> val = val * 10 + ( *cur - '0' ); >> if ( val < 0 ) /* integer overflow ? */ >> exit( EXIT_FAILURE ); > This isn't safe. First off, overflow is Undefined Behavior, > and can trap before ever getting to your test. But even > on a machine that silently wraps, as many do nowadays, > this doesn't catch about half of all overflows. Consider, > on a 16-bit system (for simplicity), 65540. After four > digits val==6554 and *cur=='0'. val*10 wraps to 4, > plus 0 is 4, is not < 0 but has overflowed.
Unfortunately, you're completely right. At least I had the "untested code" bit in there ;-) But seriously, the only working alternative I came up with is if ( val > ( INT_MAX - ( *p - '0' ) ) / 10 ) return EXIT_FAILURE; val = val * 10 + ( *p - '0' ); But this more or less requires to do the calculation twice. (And to make it work cleanly for both positive and negative integers I would need two different loops because INT_MAX isn't necessarily equal to -INT_MIN and even writing -INT_MIN might lead to trouble if INT_MAX < -INT_MIN). Any- one with an idea for a better and more elegant method? Regards, Jens -- _ _____ _____
_ | | | | | | | |_| | | | | | http://www.physik.fu-berlin.de/~toerring \___/ens|_|homs|_|oerring
|
Mon, 14 Feb 2005 05:35:54 GMT |
|
 |
David Thompso #13 / 14
|
 String converter
Quote:
... > > This isn't safe. First off, overflow is Undefined Behavior, > > and can trap before ever getting to your test. But even > > on a machine that silently wraps, as many do nowadays, > > this doesn't catch about half of all overflows. ... > Unfortunately, you're completely right. At least I had the "untested > code" bit in there ;-) But seriously, the only working alternative I > came up with is > if ( val > ( INT_MAX - ( *p - '0' ) ) / 10 ) > return EXIT_FAILURE; > val = val * 10 + ( *p - '0' ); > But this more or less requires to do the calculation twice. (And to make > it work cleanly for both positive and negative integers I would need two > different loops because INT_MAX isn't necessarily equal to -INT_MIN and > even writing -INT_MIN might lead to trouble if INT_MAX < -INT_MIN). Any- > one with an idea for a better and more elegant method?
Not really. You can avoid the division most of the time, which is usually the most costly operation (although on machines with 1->2 multiplication can and might be implemented as a scaled multiplication by the reciprocal, or perhaps by floating-point multiplication by the reciprocal if FP is available and the conversion isn't too costly), by testing first for val <= (INT_MAX/10)-1, which should be precomputed by the compiler, and handling the "teetering on the edge" cases separately (on machines with silent wrap, _these_ cases do produce a negative result). Yes, INT_MIN == -INT_MAX-1 (in 2sC) is a problem. I usually punt by converting to the string to _unsigned_ int and then worrying about the sign (although it is also theoretically possible that UINT_MAX == INT_MAX) or actually to _unsigned long_ and then worry about narrowing _or_ limiting to a range _and/or_ sign. -- - David.Thompson 1 now at worldnet.att.net
|
Fri, 18 Feb 2005 12:56:16 GMT |
|
 |
Chris Tore #14 / 14
|
 String converter
[on avoiding overflow in a strtol()-equivalent function] Quote:
>>> val = val * 10 + ( *cur - '0' ); >>> if ( val < 0 ) /* integer overflow ? */
>> This isn't safe. First off, overflow is Undefined Behavior, >> and can trap before ever getting to your test. But even >> on a machine that silently wraps, as many do nowadays, >> this doesn't catch about half of all overflows. Consider, >> on a 16-bit system (for simplicity), 65540. After four >> digits val==6554 and *cur=='0'. val*10 wraps to 4, >> plus 0 is 4, is not < 0 but has overflowed.
Quote:
>Unfortunately, you're completely right. At least I had the "untested >code" bit in there ;-) But seriously, the only working alternative I >came up with is > if ( val > ( INT_MAX - ( *p - '0' ) ) / 10 ) > return EXIT_FAILURE; > val = val * 10 + ( *p - '0' ); >But this more or less requires to do the calculation twice. (And to make >it work cleanly for both positive and negative integers I would need two >different loops because INT_MAX isn't necessarily equal to -INT_MIN and >even writing -INT_MIN might lead to trouble if INT_MAX < -INT_MIN). Any- >one with an idea for a better and more elegant method?
What I used in my strtol() implementation goes something like this: - Calculate the number that, if there is another valid digit, results in potential overflow. If the number is nonnegative and INT_MAX is 32767 and we are working in base ten, this is 3276. If the number is negative and INT_MIN is -32768 (and we are still working in base ten), the number is again 3276, although this time -3276. Call this the "cutoff" number -- inputs that exceed the cutoff definitely overflow, while inputs that exactly equal the cutoff may be OK. - Next, calculate the digit beyond which overflow occurs. For our two cases (nonnegative and negative), these are 7 and 8 respectively. Call this the limit for "cutoff numbers", i.e., an input of 3276 with a following digit of 7 (for positive) or 8 (for negative) is OK, but one with anything greater than 7 (respectively 8) is not. Note that these numbers are simply INT_MAX / 10 and INT_MAX % 10 (and similar for INT_MIN, although in C89 the rounding direction is not specified in advance). Now we have an easy test for "will overflow" (but watch out for sign inversion for negative numbers): if (there is another valid digit) { if (val > cutoff || /* e.g., val > 3276 */ (val == cutoff && digit > cutlim)) /* e.g., 3276 but digit is 9 */ ... handle would-overflow error ... else val = val * 10 + digit; } (In my code, I actually did all the work in "unsigned"s and negated at the end, I think. I would have to check.) -- In-Real-Life: Chris Torek, Wind River Systems (BSD engineering)
|
Fri, 18 Feb 2005 18:00:42 GMT |
|
|
|