I am trying to find out what this casting operation do:
char w[12] = {0};
int r;
r = (int)gets(w);
When input is for example 156, the value of r is -1073744588, so it is not a simple conversion to integer.
What your code does is cast the return value of gets, which is a pointer to the first character of the user input string, into an int. In other words, you are putting the address of the string into an integer, r. However, the address is so high that it "wraps around" in the integer r, yielding the large negative value.
You are most likely trying to convert the user's input into an int. This is the code to do so
char buf[80];
int res;
if (fgets(buf, sizeof buf, stdin)) /* load input into buf */
if (sscanf(buf, "%d\n", &res) < 1) /* try to read integer from buf */
fprintf(stderr, "Bad input\n"); /* input was bad; write error */
Notice, you should never use gets because it has serious security problems. Use fgets instead to read a line from the standard input.
Related
I'm trying to convert an inputted character to an integer by using strtol. Here's part of the source code:
char option;
char *endptr;
printf("=========================================Login or Create Account=========================================\n\n");
while(1) {
printf("Welcome to the Bank management program! Would you like to 1. Create Account or 2. Login?\n>>> ");
fgets(&option, 1, stdin);
cleanStdinBuffer();
option = strtol(&option, &endptr, 10);
In the strtol function, I'm getting a warning saying:
Clang-Tidy: Narrowing conversion from 'long' to signed type 'char' is implementation-defined
Can someone tell me what I'm doing wrong?
Clang-Tidy is warning you about the implicit conversion you are doing here where you are assign the long return value of strtol to a char:
option = strtol(&option, &endptr, 10);
If this is intentional and you are sure the value will be in the [-128,127] range that isn't necessarily an issue (it's just a warning), but even then I would advice to explicitly cast the return-type of strtol, use int8_t instead of char and not reuse the option variable for the return value. In other words:
int8_t value = (int8_t)strtol(&option, &endptr, 10);
If it wasn't intentional I would recommend you to simply use long as type for the variable you assign the return value of strtol, so:
long value = strtol(&option, &endptr, 10);
What Clang-tidy doesn't warn you about is that the first argument to strtol should be a pointer to a char buffer containing a 0-terminated string, not a pointer to a single char. This is also an issue for fgets. There are two ways to solve this, either:
Make option a char array of at least two chars,
Use fgetc instead and modify your code into something like this:
int option = fgetc(stdin);
if (option == '1') {
/*Create Account */
} else if (option == '2') {
/* Login */
}
else {
/* Error */
}
I think the latter looks much cleaner.
char can only hold a very little subset of the long values. strtol returns long and you assign it to char.
This call of fgets
fgets(&option, 1, stdin);
always sets the character option to the terminating zero character '\0' provided that the user did not interrupt the input.
Here is a demonstrative program.
#include <stdio.h>
int main(void)
{
char c = 'A';
printf( "Before calling fgets c = %d\n", c );
fgets( &c, 1, stdin );
printf( "After calling fgets c = %d\n", c );
return 0;
}
The program output is
Before calling fgets c = 65
After calling fgets c = 0
independent on what the user will enter. Here is the value 65 is the ASCII code of the character 'A' that was stored in the variable c before calling fgets.
If you want to enter a character then you should use
scanf( " %c", &input );
Pay attention to the blank before the conversion specifier.
After this call you can check whether the user typed a digit like
#include <ctypes.h>
//...
if ( isdigit( ( unsigned char )input ) ) input = input - '0';
and set the variable input to the corresponding integer value in the range [0, 9].
I tried code below, but the results aren't correct. I think that something with size of buffer can be implemented in a wrong way.
int f(int* as) {
*as = read(STDIN_FILENO, as, sizeof(int));
} //i print 123
int s;
f(&s);
printf("%d",s); // prints 4
Two things that prevent the program to give the result you expect
1) using read to read from standard input (characters) and store that into a (binary) integer
2) storing the result of read into that same integer, overwriting the (wrong) value stored in 1)
Have a look at scanf or fgets (atoi...), to read into a character array (string), then convert the characters read into a binary number, for instance
char str[20];
fgets(str, 20, stdin);
int s = atoi(str);
read version
char str[20];
ssize_t nread = read(STDIN_FILENO, str, 20);
int s = atoi(str);
See what an integer is,
I'm trying to find a way to check if a read-in char * can be represented as a number (with or without decimal places) or not. Essentially I am asking, if I have a text file containing the following:
-1.9e-3
e9
1e9
1ee9
-1-.9e3
.9e3
.9.e3
It would be able to recognize that line 1, line 3, and line 6 can be "enumerated" into valid numbers, whereas all of the other lines contain erroneous inputs. I know this could be done with brute force, but there is potentially an unlimited number of possibilities that could be wrong. It would be much easier if there was a function that read in the entire char * and can just say, "Yes that string of characters can be represented as an actual number" or "No that string of characters cannot be turned into the number that it intends to be."
And by enumerated I mean that the string (char *) can be the number that it wishes to represent.
Just use strtold(), it will tell you if it succeeds, and also give you the converted number.
Just try to convert it with strtold() and use the possibilities to check for errors, e.g.
char *x = "-1.9e-3";
errno = 0;
char *endptr;
long double xnum = strtold(x, &endptr);
if (*endptr) // (&& *endptr != '\n' if you read with `fgets()`)
{
// extra / invalid characters;
}
else if (errno == ERANGE)
{
// out of range;
}
Two part question;
I'm Coming from a high level Language, so this is a question about form not function;
I've written an isnumeric() function that takes a char[] and returns 1 if the string is a number taking advantage of the isdigit() function in ctype. Similar functions are builtin to other languages and I have always used something like that to integrity check the data before converting it to a numeric type. Mostly because some languages conversion functions fail badly if you try to convert a non-number string to an integer.
But it seems like a kludge having to do all that looping to compensate for the lack of strings in C, which poses the first part of the question;
Is it acceptable practice in C to trap for a 0 return from atoi() in lieu of doing an integrity check on the data before calling atoi()? The way atoi() (and other ascii to xx functions) works seems to lend itself well to eliminating the integrity check altogether. It would certainly seem more efficient to just skip the check.
The second part of the question is;
Is there a C function or common library function for a numeric integrity check
on a string? (by string, I of course mean char[])
Is it acceptable practice in C to trap for a 0 return from atoi() in lieu of doing an integrity check on the data before calling atoi()?
Never ever trap on error unless the error indicates a programming error that can't happen if there isn't a bug in the code. Always return some sort of error result in case of an error. Look at the OpenBSD strtonum function for how you could design such an interface.
The second part of the question is; Is there a C function or common library function for a numeric integrity check on a string? (by string, I of course mean char[])
Never use atoi unless you are writing a program without error checking as atoi doesn't do any error checking. The strtol family of functions allow you to check for errors. Here is a simply example of how you could use them:
int check_is_number(const char *buf)
{
const char *endptr;
int errsave = errno, errval;
long result;
errno = 0;
result = strtol(buf, &endptr, 0);
errval = errno;
errno = errsave;
if (errval != 0)
return 0; /* an error occured */
if (buf[0] == '\0' || *endptr != '\0')
return 0; /* not a number */
return 1;
}
See the manual page linked before for how the third argument to strtol (base) affects what it does.
errno is set to ERANGE if the value is out of range for the desired type (i.e. long). In this case, the return value is LONG_MAX or LONG_MIN.
If the conversion method returns an error indication (as distinct from going bananas if an error occurs, or not providing a definitive means to check if an error has occurred) then there is actually no need to check if a string is numeric before trying to convert it.
With that in mind, using atoi() is not a particularly good function to use if you need to check for errors on conversion. Zero will be returned for zero input, as well as an error, and there is no way to check on why. A better function to use is (assuming you want to read an integral value) is strtol(). Although strtol() returns zero on integer, it also returns information that can be used to check for failure. For example;
long x;
char *end;
x = strtol(your_string, &end, 10);
if (end == your_string)
{
/* nothing was read due to invalid character or the first
character marked the end of string */
}
else if (*end != '\0`)
{
/* an integral value was read, but there is following non-numeric data */
}
Second, there are alternatives to using strtol(), albeit involving more overhead. The return values from sscanf() (and, in fact, all functions in the scanf() family) can be checked for error conditions.
There is no standard function for checking if a string is numeric, but it can be easily rolled using the above.
int IsNumeric(char *your_string)
{
/* This has undefined behaviour if your_string is not a C-style string
It also deems that a string like "123AB" is non-numeric
*/
long x;
char *end;
x = strtol(your_string, &end, 10);
return !(end == your_string || *end != '\0`);
}
No (explicit) loops in any of the above options.
Is it acceptable practice in C to trap for a 0 return from atoi() in lieu of doing an integrity check on the data before calling atoi()?
No. #FUZxxl well answers that.
Is there a C function or common library function for a numeric integrity check on a string?
In C, the conversion of a string to a number and the check to see if the conversion is valid is usually done together. The function used depends on the type of number sought. "1.23" would make sense for a floating point type, but not an integer.
// No error handle functions
int atoi(const char *nptr);
long atol(const char *nptr);
long long atoll(const char *nptr);
double atof(const char *nptr);
// Some error detection functions
if (sscanf(buffer, "%d", &some_int) == 1) ...
if (sscanf(buffer, "%lf", &some_double) == 1) ...
// Robust methods use
long strtol( const char *nptr, char ** endptr, int base);
long long strtoll( const char *nptr, char ** endptr, int base);
unsigned long strtoul( const char *nptr, char ** endptr, int base);
unsigned long long strtoull( const char *nptr, char ** endptr, int base);
intmax_t strtoimax(const char *nptr, char ** endptr, int base);
uintmax_t strtoumax(const char *nptr, char ** endptr, int base);
float strtof( const char *nptr, char ** endptr);
double strtod( const char *nptr, char ** endptr);
long double strtold( const char *nptr, char ** endptr);
These robust methods use char ** endptr to store the string location where scanning stopped. If no numeric data was found, then *endptr == nptr. So a common test could is
char *endptr;
y = strto...(buffer, ..., &endptr);
if (buffer == endptr) puts("No conversion");
if (*endptr != '\0') puts("Extra text");
If the range was exceed these functions all set the global variable errno = ERANGE; and return a minimum or maximum value for the type.
errno = 0;
double y = strtod("1.23e10000000", &endptr);
if (errno == ERANGE) puts("Range exceeded");
The integer functions allow a radix selection from base 2 to 36. If 0 is used, the leading part of the string "0x", "0X", "0", other --> base 16, 16, 8, 10.
long y = strtol(buffer, &endptr, 10);
Read the specification or help page for more details.
You probably don't need a function to check whether a string is numeric. You will most likely need to convert the string to a number so just do that. Then check if the convertion is successful.
long number;
char *end;
number = strtol(string, &end, 10);
if ((*string == '\0') || (*end != '\0'))
{
// empty string or invalid number
}
the second argument of strtol is used to indicate where the parsing ended (the first non-numeric character). That character will be \0 if we've reached the end of the string. If you want to permit other characters after the number (like ), you can use switch to check for it.
strtol works with long integers. If you need some other type, you should consult the man page: man 3 strtol. For floating-point numbers you can use strtod.
Don't trap if the program logic permits that the string is not numeric (e.g. if it comes from the user or a file).
OP later commneted:
I'm looking for a way to determine if the string contains ONLY base 10 digits or a decimal or a comma. So if the string is 100,000.01 I want a positive return from func. Any other ascii characters anywhere in the string would result in a negative return value.
If is all your interest, use;
if (buffer[strspn(buffer, "0123456789.,")] == '\0') return 0; // Success
else return -1; // Failure
I am reading formatted data with
sscanf(buf,"%d %d",&a,&b);
data format = (unsigned short SPACE unsigned short);
but when i read it only successfully extract 2nd integer value from buffer for first it extracts 0. I am checking with (printf("nRecvd %d and %d",a,b)) I am reading from socket and I verified buffer values. All is ok untill sscanf.
char buf[MAXBUFL];
unsigned short a, b;
if (sscanf(buf,"%d %d",&a,&b) != 2)
snprintf (buf, sizeof(buf), "data error\r\n");
else
{
printf("\nRecvd %d and %d",a,b);}
Any suggestions?
You are scanning into short variables which means that you must tell sscanf that. You need:
if (sscanf(buf,"%hd %hd",&a,&b) != 2)
This is explained in the man page for scanf.