Those are reserved for people who value speed over correctness.
Those are reserved for people who value speed over correctness.
First time I heard it was about 1993.
> C has perfecly well defined semantics for (eg) null pointer dereference: read or write memory location zero, and consequently probably crash.
C does have "perfectly well-defined semantics" for null pointer dereference, but it's undefined behavior. Sure, the null pointer happens to be 0 on the vast majority of architectures most programmers work with, but apparently non-zero null pointers are still used these days (at least in the admgcn LLVM target, from what I understand after a quick glance at the linked page), so it's not even a "all sane modern machines" thing [0].
In any case, I'd love to see some good benchmarks showing the effect of the more "controversial" optimizations. Compiler writers say they're important, people who don't like these optimizations tend to say that they don't make a significant difference. I don't really feel comfortable taking a side until I at least see what these optimizations enable (or fail to enable). I lean towards agreeing with the compiler writers, but perhaps that's because I haven't been bitten by one of these bugs yet...
[0]: https://reviews.llvm.org/D26196Personally, I mostly use C/C++ for fairly high performance numerical code and happen to benefit greatly from all the "unsafe" stuff, including optimizations only enabled by compilers taking advantage of undefined behavior. I'm therefore naturally inclined to strongly oppose any attempts to eliminate undefined behavior from the language standards. At the same time, however, I fully recognize that most people would probably benefit from a safer set of compiler defaults (as well as actually reading the compiler manuals once in a while) or even using languages with more restrictive semantics. Ultimately, there is no free lunch and performance doesn't come without its costs.
... no, C explicitely has no well defined semantics since it's undefined behaviour. You may believe that C do due to habit but that's not the case.
when you program in C you program against the C abstract machine, not against a particular hardware.
#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?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.
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...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.
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.
"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 thisThat 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.
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.
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.
(And you're missing a few error checks.)
So yes, they kept the verified code small, so that they could focus on all of the tooling. I'm unconvinced that formal methods intrinsically can't scale.
Also, I'd add that when you say
> We already have a good automated test culture, but they can only prove everything we thought of works, we have lots of issues with situations we didn't think about
in a lot of ways formal verification only helps you with situations you know about, specifically problem classes that you know about. For instance sel4 was susceptible to Meltdown.
Almost nobody does because formal code writing forces a constraint / logic / contract thinking which is alien to many alleged programmers.
It could be taught. Afterwards, compare performance and quality of the result. Could be a big competitive advantage...
The fact that after four decades it has not proven to be so is evidence that either it is very difficult to put into use or not that effective.
"C is for speed only"... Do you not realize there is an entire embedded world that runs on C?
https://www.ptc.com/en/products/developer-tools/objectada
https://www.ghs.com/products/AdaMULTI_IDE.html
http://www.astrobe.com/default.htm
Just a couple of examples from many more, after all I don't want to overflow you with information.
I call that production deployment.
As for myself, feel free to believe whatever makes you feel better.