#include <stdio.h>
int main() {
int a, b, c;
printf("Please enter two numbers: ");
scanf("%d %d", &a, &b);
c = a + b;
printf("The sum of the numbers you entered is %d\n", c);
return 0;
}
This is the problem. There is no non-trivial C code that doesn't use some undefined behavior somewhere. And it works just fine, right now. But who says it will on the next version of the compiler?That is not good. It's hard to reason about such a program, in other words can you trust results of such a program? How do you know whether your input caused Ub at some point or not?
The burden of sanitizing the input is on the user (either programmer or the data provider).
Checking this is usually a performance tradeoff, so it was decided not to be done by default.
(And you're missing a few error checks.)
in this precise case, (in C++ because I can't be arsed to search the modifier for int64), fairly easily :
#include <fmt.h>
#include <cstdio>
int main() {
int a{}, b{};
int64_t c{};
fmt::print("Please enter two numbers: ");
scanf("%d %d", &a, &b);
c = a + b;
fmt::printf("The sum of the numbers you entered is {}\n", c);
return 0;
}
in the case where a, b, were also of the largest int type available, you could do : #include <stdio.h>
int main() {
int a, b, c;
printf("Please enter two numbers: ");
scanf("%d %d", &a, &b);
if(__builtin_add_overflow(a, b, &c))
{
printf("The sum of the numbers you entered is %d\n", c);
}
else
{
printf("You are in a twisty maze of compiler features, all different");
exec("/usr/bin/0ad");
}
return 0;
}
but you could also use the more portable macros provided in emacs (100 % independent of any other code) : https://github.com/jwiegley/emacs-release/blob/adfd5933358fd...https://blog.regehr.org/archives/1139
HN discussion:
https://news.ycombinator.com/item?id=7665254
Your first suggestion is similar to his checked_add_1(). The Emacs macros are similar to his checked_add_4(). Presumably builtins are optimal, performance wise, for a given compiler.
FWIW, I tried this example:
#include <stdio.h>
#include <limits.h>
#include <stdint.h>
int main () {
int64_t x;
int a = INT_MAX, b = 1;
x = a + b;
printf( "%lu %lu %d %lld\n", sizeof(x), sizeof(a), a, x);
return 0;
}
After compiling gcc -std=c11 -fsanitize=undefined -Wall -Wpedantic -Wextra (getting no warnings), on running I get c-undef.c:8:9: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
8 4 2147483647 -2147483648
Also with -O0 and -O3.At least according to the answer in [1], signed (but not unsigned) integer overflow is undefined behavior (unless C and C++ have diverged on this matter, but I get the same result when the same source code is compiled as C++17.)
This may seem to be pedantic (if it is not just wrong), but the point is that you have to be unremittingly pedantic to avoid undefined behavior.
[1]https://stackoverflow.com/questions/16188263/is-signed-integ...
that's 100% correct, nice catch.
"Portable C" doesn't exist.
in C++ you'd just add
static_assert(sizeof(int) < sizeof(int64_t), "fix your types");
to be safe about thisIt's a performance hit though.
I don't think you'd want a banking app written with assumption that you'll never have more than MAXINT amount of dollars, and adding few more will roll you back to 0 or even put you in debt... silently... because well it works this way, and designer didint think you'll hit the limit. If it ever happened you'd like a siren to go off.
Yes, and it could also decide to wipe your hard drive because of the latitude given to it by the C standard. Many compilers have an option to enable some sort of special behavior when a signed integer overflows, but such extensions are non-standard.
Reference: N1570 (C11) 7.21.6.2 paragraph 10: "If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the object, the behavior is undefined."
strtol() avoids this problem.