5 ms·
I have a small program which uses an undefined behavior. How would you rewrite this so as to not do so, but maintain all existing defined behaviors? #incl
by chowells 8y ago
I have a small program which uses an undefined behavior. How would you rewrite this so as to not do so, but maintain all existing defined behaviors?
#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?
- dpc_pw 8y agoWhat's the UB here?
- jcelerier 8y agoconsider what happens if the input numbers are INT_MAX and 1
- saagarjha 8y agoThat doesn't make the program incorrect, it just means that you can't provide that particular input to the program.
- junke 8y agoYes, but the "can't" really is a "shouldn't".
- saagarjha 8y agoPoint taken; unfortunately I can't edit anymore…
- jcelerier 8y agoa program which has undefined behaviour on some inputs is an incorrect program by definition
- saagarjha 8y agoI'll have to disagree with you there: you cannot fully control the range of inputs that you are provided at all times, or perform verification of input. But you can clearly document what conditions the input must satisfy for the program to be well-defined.
- luk32 8y agoThe problem is that UB is invoked on runtime for particular inputs... And it silently makes program unreasonable after this point. 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.
- saagarjha 8y agoThat's a documentation/sanitization problem, rather than a one involving the question of whether this program is well-formed. My response was that it is, given the input obeys the rules you've stated. How you enforce that is a different concern (or whether you dislike the preconditions and think this code could be more robust and have a wider domain, because it certainly could).
- deleted 8y ago[deleted]
- AstralStorm 8y agoUnless the undefined behaviour is hidden in printf or scanf, this code exhibits none. Variadic functions are reasonably well defined and no type aliasing takes place. (And you're missing a few error checks.)
- saagarjha 8y agoCan the people downvoting this comment provide a reason why? It seems perfectly reasonable to me.
- deleted 8y ago[deleted]
- jcelerier 8y agoInteger overflow is undefined behaviour so an input for instance of 2147483647 and 1 makes the program invalid.
- saagarjha 8y agoSure, but the program is well-defined on inputs that do not cause it to overflow. It does not perform undefined behavior in those cases, so it's correct given those preconditions.
- jcelerier 8y ago> I have a small program which uses an undefined behavior. How would you rewrite this so as to not do so, but maintain all existing defined behaviors? 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/adfd5933358fdf6715423dee8169eb3cd7d982db/lib/intprops.h https://github.com/jwiegley/emacs-release/blob/adfd5933358fd...
- SloopJon 8y agoJohn Regehr wrote a post about checking for signed overflow a few years ago: https://blog.regehr.org/archives/1139 https://blog.regehr.org/archives/1139 HN discussion: https://news.ycombinator.com/item?id=7665254 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.
- mannykannot 8y agoWith regard to your first version: I had this vague feeling that the usual arithmetic conversions state that if the operands are of the same type, then no conversion is performed, and apparently this is so. If so, then would not the expression a + b potentially overflow, regardless of what it is being assigned to? 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-integer-overflow-still-undefined-behavior-in-c https://stackoverflow.com/questions/16188263/is-signed-integ...
- saagarjha 8y agoThe compiler will not break this example, because it cannot put restrictions on the values of a and b. The only thing it can do is assume that a + b < INT_MAX, in which case the program will work as expected.
- luk32 8y agoNo. It can check whether the operands will cause overflow and issue an error instead of silently giving a bogus result. It'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.
- saagarjha 8y ago> It can check whether the operands will cause overflow and issue an error instead of silently giving a bogus result. 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.
- _kst_ 8y agoNobody has mentioned that scanf has undefined behavior on numeric overflow. If INT_MAX is 2147483647 and the user enters 2147483648, the behavior is undefined. (That doesn't just mean you can get an arbitrary value for a and/or b. It means that the standard says nothing at all about how the program behaves). 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.