Arithmetic overflow when using atoi() on argv[1] in C - c

I am using atoi(argv[1]) Here is a snippet of my code
void main(int argc, char* argv[])
{
int evenOrOdd = 0;
int inputtedNum = 0;
pid_t pid;
int i;
char buf[BUF_SIZE];
if (argc != 2)//make sure user has input correctly entered
{
printf("Please use input: ./a.out #\n");
}
else
{
inputtedNum = atoi(argv[1]);
if(inputtedNum < 0) //make sure number is positive
{
printf("Please use a positive number.\n");
return;
}
}
// ...
}
The code goes on and whatnot, but when entering some numbers, it will sometimes believe they're negative. If I enter 123456789 it will have no problem and run the program correctly. If I enter 12345678910 It will think the number is a negative number. Am I running into some buffer error here to where the negative flag flops? I'm not quite certain how to fix it.
Any and all help is appreciated!

int is a relatively narrow integer type, and also it is a signed type. Use unsigned long for inputtedNum and strtoul(argv[1], 0, 0) to parse the number.

On most compilers, the largest non-negative integer type supported in C is a 64-bit unsigned integer.
12345678910 cannot be stored in the int type that you are using, even if it was unsigned long.
You can solve this issue by "dividing" the input string in argv[1] into two strings, when it is longer than 8 digits, use atoi on each string, and combine the results into an unsigned long long variable.
This will extend your positive range from approximately 4 billion, to the largest 16-decimal-digit number.
From main, call readNum(argv[1]):
unsigned long long readNum(const char* input)
{
int length = strlen(input);
if (length <= 8)
{
return atoi(input);
}
else if (length <= 16)
{
char str1[8+1] = {0};
char str2[8+1] = {0};
memcpy(str1,input,8);
memcpy(str2,input+8,length-8);
return 100000000ULL*atoi(str1)+atoi(str2);
}
return 10000000000000000ULL; // Invalid input
}
Keep in mind that in order to print an unsigned long long, you need to use printf with %llu.

Related

How to limits value user input < INT_MAX in C

#include <stdio.h>
#include <stdlib.h>
#include<limits.h>
int getInt() {
int n, check;
char ch;
do {
fflush(stdin);
printf("Enter positive integer n = ");
check = scanf("%d%c", &n, &ch);
if ((check == 2)&&(ch == '\n')) {
if (n > INT_MAX) {
printf("Invalid number\n");
} else if (n < 0) {
printf("n must > 0");
} else {
break;
}
} else printf("Invalid number\n");
} while (1);
return n;
}
int main(int argc, char** argv) {
int n;
n = getInt();
}
My code accepts user input number in range 0 to INT_MAX.
When I input -1, program displays "n must > 0".
But when I input '77777777777777777777777777777777' (> INT_MAX) program still displays "n must > 0", and not 'Invalid number'.
With out of range input in scanf("%d%c", &n, &ch);, the behavior is undefined.
Instead read a line of input with fgets(), then quality using strtol()
for (;;) {
char buf[100];
if (fgets(buf, sizeof buf, stdin) == NULL) {
printf("No more input\n");
return -1;
}
errno = 0;
char *endptr;
long val = strtol(buf, &endptr, 0);
// No numeric conversion done at all?
// Numeric input outside long range?
// Junk after the numeric text?
if (buf == endptr || errno == ERANGE || *endptr != '\n') {
printf("Invalid number\n");
continue;
}
// Outside int range?
if (val < INT_MIN || val > INT_MAX) {
printf("Invalid number\n");
continue;
}
if (val < 0) {
printf("n must > 0");
continue;
}
n = (int) val;
}
I'd recommend a re-usable int get_int(int *val) helper function.
First you need to understand how variables stores data.
In 64 bits architeture, the int type have 4 bytes (either the C long type, regardless architeture), so can store the following values:
00000000 00000000 00000000 00000000 = 0 (decimal value)
01111111 11111111 11111111 11111111 = 2,147,483,647 (decimal value)
11111111 11111111 11111111 11111111 = 4,294,967,294 (unsigned decimal value)
11111111 11111111 11111111 11111111 = -1 (signed decimal value)
Note that the integer types can use the Most Significant Bit (MSB) to represent signal (0 to positive, 1 to negative) using a modular aritmetic.
For more details about integer signal: https://en.wikipedia.org/wiki/Two%27s_complement
So, to store decimal data higher than INT_MAX, you need more bytes than you have using int type. A good way, compatible with 64 bits architeture, is using long long type.
Long long type uses 8 bytes, so can stores value higher than INT_MAX.
You will have to declare:
long long n;
And use scanf() like this:
scanf("%lld%c", &n, &ch);
Your fflush(stdin) have to be after the scanf(), because if your aplication break the loop after the scanf() and before have reached the fflush() instruction you may have problem in further input handling. Like this:
check = scanf("%lld%c", &n, &ch);
fflush(stdin);
However, some developers disapprove use fflush() in stdin, so this is an alternative (a bit more complex) using getch() accepting only numbers and converting char* to long long using strtoll():
char c = 0;
char* input_number = malloc(32);
int accepted_chars = 0;
memset(input_number, 0, 32);
while(c != '\r'){ //loop until user press ENTER
c = getch();
//receive numbers keys pressed
if(c >= '0' && c <= '9'){
*(input_number + accepted_chars) = c;
accepted_chars ++;
printf("%c", c);
}
//receive backspace key pressed
if(c == 8){
if(accepted_chars > 0){ //don't do nothing if there is nothing to clear
accepted_chars --;
*(input_number + accepted_chars) = 0;
printf("\b");
printf(" ");
printf("\b");
}
}
}
printf("\n");
char* endptr;
n = strtoll(input_number, &endptr, 10); //convert string in base 10 (decimal) long long type
Just checking
int x;
...
if (x > INT_MAX) { ...
is something that will never work. If an int value cannot have values above INT_MAX then it is impossible that you can ever get x stored a value higher than that. So that if will become always false and the compiler will probably eliminate all the code you put inside that if's then block.
Normal reading routines like scanf() actually limit the input to a value in the range of allowable values.
But if you want to read and build the number yourself, you need to ancitipate the possibility, and use better triggers to stop.
For example, checking that the number is above (INT_MAX - 9) / 10 will tell you that if you try to add another digit to it, you'll run the risk of overflowing the integer, when you add the digit. You can simply stop there and don't continue reading, but if you want to read one more digit (who knows, it could be a 0 and that doesn't hurt, you'll have to check for something like
int digit;
int the_integer = 0;
while ( (digit = fgetchar(stdin)) != EOF
&& isdigit(digit)
&& the_integer <= (INT_MAX - (digit - '0')))
{
the_integer *= 10;
the_integer += digit;
} /* while */
if (digit != EOF && isdigit(digit)) {
/* x > (INT_MAX - (digit - '0')) */
/* this is necessary, as if you don't do it, you'll lose
* the next character to read. */
unput(digit, stdin);
}
This way you'll check the number x before multiplying it by 10 and adding the digit - '0' value.
As pointed out in the comments section, there is no point in testing if n > INT_MAX, as n is of type int and therefore by definition unable to represent any value larger than INT_MAX.
A simple, flexible way of checking the size of a number provided as user input would be to read it in as a string and count the number of digits by using the function strlen and possibly also isdigit to count the actual digits.
However, when dealing with potentially large numerical values as user input, it is usually better to first read this input into a data type that is larger than int and do your range checks with this larger data type. After you are satisfied that the number is in the desired range, you can convert it to the smaller int data type. Although the ISO C standard does not guarantee that a long long is larger than an int, this is a reasonable assumption and it is the case on all compilers that I am aware of. However, on some platforms (including 64-bit Windows), the data type long is the same size as int, so a long cannot be reliably used for this on all platforms.
Probably the easiest solution to your problem would be to use the function strtol or strtoll. Using these functions has the advantage that you don't need a larger data type and it explicitly tells you if a number is out of range, by setting errno to ERANGE. However, these functions only support the data types long and long long, but not int. Therefore, if you want to use int, you will have to do your range checks manually before you convert it.

Digit count function for C programming won't work past 11 digits

The following function is supposed to be used to count the number of digits in a number. Unfortunately it doesn't work for numbers with 11 or more digits and I'm not sure why. I feel like it has something to do with the data type but I thought long long int would be fine in this case. Thanks for the help!
long long int getLength(long long int input)
{
long long int length = 0;
while(input != 0)
{
input /= 10;
++length;
}
return (length);
}
This may not stand as an answer, but allow me to report output here. Can you double check? It works on my side.
#include "stdio.h"
long long int getLength(long long int input)
{
long long int length = 0;
while(input != 0)
{
input /= 10;
++length;
}
return (length);
}
int main()
{
printf("%lld\n", getLength(12345678901)); // 11
printf("%lld\n", getLength(123456789012)); // 12
printf("%lld\n", getLength(1234567890123)); // 13
printf("%lld\n", getLength(0)); // 0
printf("%lld\n", getLength(-123)); // 3
}
Platform Windows 10, and gcc --version returns
gcc (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.3.0
Question 1: do you really need type long long int to report number of digits?
Question 2: did you correctly use the format specifier %lld in your printf?
Do you really need long long int for the length? I mean it's the length of the number, NOT the number itself.
This code works fine in my compiler.
#include <stdio.h>
int getLength(long long int input)
{
int length = 0;
while(input != 0)
{
input /= 10;
++length;
}
return (length);
}
int main() {
printf("Length of number : %d\n", getLength(12345678901234));
return 0;
}
Output:
Length of number : 14
Program ended with exit code: 0

Pointers ignoring unsigned trait in C?

I'm curious why the following code returns negative numbers even though every single variable and fuction initialization in this program is an unsigned int. I thought they always were supposed to be positive?.
#include <stdio.h>
unsigned int main ()
{
unsigned int ch = 0;
unsigned int asteriskValue = 0;
while(ch!=27)
{
ch = getch();
if (ch == 224)
{
increaseDecrease(&asteriskValue);
}
printf("%d",asteriskValue);
}
}
void increaseDecrease(unsigned int *value)
{
unsigned int upOrDown;
upOrDown = getch ();
if(upOrDown == 72)
(*value)--;
else if(upOrDown == 80)
(*value)++;
}
I think your results is really a positive value, but printing it with a "%d" flag, tells printf to interpret it as a signed value, as if you were casting on the fly the type: (signed)asteriskValue
Try printing it with a "%u" formatting instead of "%d".

function convert string to int - C

I want to scan in a string that can take at least 200 characters and then I want to convert the string to an int, so that I can print it with e.g. printf("%d", digit).
How can I write a function kinda like this I've written here
(this one does not work!):
int main()
{
char car[200];
int number;
int i,x;
int sum = 0;
printf("Write in number: \n");
scanf("%c", &car);
for (i=0; i<200; i++) {
if (car[i] != '\0') {
x = car[i]-'0';
sum = sum + x;
if (i != 0) {
sum = sum*10;
}
}
}
return 0;
}
first:
scanf("%c", &car);
from man scanf:
c
Matches a sequence of characters whose length is specified by the maximum field width (default 1); the next pointer
must be a pointer to char, and there must be enough
room for all the characters (no terminating null byte is added). The usual skip of leading white space is suppressed. To
skip white space first, use an explicit space
in the format.
So you're reading exactly one character, not a whole string.
Then, you rely on a terminating null byte being added, which isn't happening. Use %199s instead, leaving enough room for the terminating null byte.
Then, considering no int in this world should have enough space for numbers with 199 decimal digits, you should think about your 200 character buffer.
If your goal is not to write such a function for educational purposes, but because you need one:
int number;
scanf("%d", &number);
does exactly that: Read one number from the input, and place it in number.
scanf("%s", car);
You need to read a string %s not a single char %c. Also char array will decay to a pointer when passed as an argument, so you shouldn't use &char.
Here is the solution,Start from the end of the string because units place is from right to left then increment the units place to ten's place then 100 and so on.
#include<stdio.h>
#include<string.h>
int main()
{
char input[9];
int digit,number=0,i=1;
printf("Enter Number:\n");
scanf("%s",input);
digit=strlen(input)-1;
while(digit>=0)
{
number=number + i*(input[digit]-'0');
digit=digit-1;
i=i*10;
}
printf("%d",number);
return 0;
}
Close. Suggested changes below.
Use char car[200+1] to "take at least 200 characters".
Use "%200s" rather than "%c" to read a string rather than a char and to limit its input.
//int main()
int main(void) {
// char car[200];
char car[200+1];
int number;
int i,x;
int sum = 0;
printf("Write in number: \n");
// scanf("%c", &car);
scanf("%200s", car);
// for (i=0; i<200; i++) {
for (i=0; car[i]; i++) {
// if (car[i] != '\0'){
x = car[i]-'0';
// sum = sum + x;
sum = 10*sum + x;
// if (i != 0) { sum = sum*10; }
}
printf("%d\n", sum)
return 0;
}
Unless input is like "000000000000000000000000000123", 200 digits will certainly overflow sum.
To detect that
x = car[i]-'0';
if (sum >= INT_MAX/10 && (sum > INT_MAX/10 || x > INT_MAX%10)) {
x = INT_MAX;
// maybe set an error flag
break;
}
sum = 10*sum + x;
An int has an maximum value of INT_MAX from #include < limits.h>. It is at least 32767. Some platforms use 16-bit, 32-bit or 64-bit int Other ranges are possible. Let us assume a worst case of 128-bit. That would need about 39 decimal digits. Leaving room for a sign and terminating null character, suggest
char car[39+1+1];

Hexadecimal number comparison?

I'm a new to C language and a bit confused about how hexadecimal, decimal numbers work. I am trying to work with maximum of 4 bytes, so 0xFFFFFFFF. I basically want the program to give an alert if any number is entered with 9 digits or higher. I have written a code as below but it still lets me input numbers bigger than 0xFFFFFFFF. What am I doing wrong? Thanks in advance!
int main()
{
uint32_t rgba;
printf("Enter rgba value: ");
scanf("%x", &rgba);
if (rgba > 0xFFFFFFFF){
printf("Maximum number of 8 digits!\n");
}
else{
rgba_values(rgba);
}
return 0;
}
As #Jongware commented, "You cannot check for a number larger than it is possible to store."
To detect if user input is outside the range of uint32_t rgba, code needs to understand that the user input has or will exceed that range.
Method 1: Use larger integer type: This method will detect "100000000" as too large an input, but will have trouble with numbers larger than "FFFFFFFFFFFFFFFF".
unsigned long long UserInput;
uint32_t rgba;
printf("Enter rgba value: ");
if (scanf("%llx", &UserInput) != 1) {
puts("EOF or Scan Failure");
return 1;
}
if (UserInput > 0xFFFFFFFFu) {
puts("Range error");
return 1;
}
rgba = UserInput;
rgba_values(rgba);
Method 2: Use fgets()/strtoul(). The following will work for input up to 99 char long (which includes the '\n').
uint32_t rgba;
char *buf[100];
printf("Enter rgba value: ");
if(fgets(buf, sizeof buf, stdin) == NULL) {
puts("EOF");
return 1;
}
char *endptr = 0;
errno = 0;
unsigned long ul = strtoul(buf, &endptr, 16);
if (buf == endptr) {
puts("No conversion");
return 1;
}
if (errno || ul > 0xFFFFFFFFu) {
puts("value out of range");
return 1;
}
rgba = (uint32_t) ul;
rgba_values(rgba);
Other methods include counting the number of hexadecimal characters or converting user input 1 char at a time.
if (rgba > 0xFFFFFFFF)
rgba is a 32-bit unsigned integer value, the condition above is always false. A 32-bit unsigned integer value is always <= 0xFFFFFFFF.
Check the return value of scanf: it returns 1 if and only if the input is a valid hexadecimal number without overflow. Note that this will only fit your purpose if the int is exactly 32 bits on your platform.
What you could do is store the user-input into a char* first:
char rgba_str[20]; // 20 is an arbitrary larger buffer size
printf("Enter rgba value: ");
scanf("%s", &rgba_str);
Then check and see if the length of the string is greater than 9 digits:
if ( strlen( rgba_str ) > 8 )
// Error
If not, turn rgba_str into a hexadecimal integer:
else
rgba = strtoul(rgba_str, NULL, 16);

Resources