Live data from Hacker News

C and C++ Aren't Future Proof

blog.regehr.org

31–40 of 108 posts

Re: C and C++ Aren't Future Proof

#31
post #13
post #5

> This propensity for today’s working programs to be broken tomorrow is what I mean when I say these languages are not future proof. It doesn't matter. This is not how programming works in the real world. In the real world, you write the most correct program you can under time pressure. A new compiler, operating system, or platform arrives that exposes a bug. You fix it and you move on. It doesn't matter if the langu…

I agree that programmers should not take on the burden of supporting hypothetical future compiler optimizations (if that's what you're saying), but this problem could be reduced if compilers started forbidding undefined behavior — then programmers would only have to adapt once.

Much undefined behaviour can't be statically detected, unfortunately.

Re: C and C++ Aren't Future Proof

#32
I think there is an important point here, which is that C and C++ compilers have let us get away with a lot of undefined behavior for a long time, and that there hasn't been a lot of tooling to help avoid it nor a culture that stresses the long-term danger of depending on it.

I can speak as someone who has been programming in C and C++ for over ten years, but only in the last few years became aware of this issue and started taking it seriously. Five years ago I would do things like cast function pointers to void-pointer and back, or calculate addresses that were outside the bounds of any allocated object and compare against them, all without really even realizing I was doing something wrong.

I don't think this will spell doom-and-gloom for C and C++ though. I think a few things will happen.

First of all, the compiler people are walking a fine line; yes, they are breaking code that relies on undefined behavior, but they often avoid breaking too much. For example, I've had it explained to me that at least for the time being, gcc's LTO avoids breaking any programs that would work when compiled with a traditional linker. In addition, they often provide switches that preserve traditional semantics for non-compliant code that needs it (like -fno-strict-aliasing and -fwrapv).

Secondly, I believe that tooling will get better, and rather than ignoring the warnings I believe that people's general awareness of this issue will raise, as well as knowledge of standard-compliant ways of working around common patterns of undefined behavior. For example, it's often easy to avoid aliasing problems by using memcpy(), and this can usually be optimized away.

Thirdly, I expect that the standard may begin to define some of this behavior. For example, I think that non-twos-complement systems are exceedingly rare these days; I wouldn't be surprised if a future version of the standard defines unsigned->signed conversions accordingly.

Re: C and C++ Aren't Future Proof

#33
post #26

Hey, don't lump C++ in with this. If you write code in the STL weenie style or the Pretend It's Java style there aren't any idioms I know of that would ever violate the rules he mentions (out-of-range pointers, signed overflow, invalid aliasing). I don't do those things and the C++ programmers I work with don't do those things, at least not habitually. I don't see violations of undefined behavior rules, or the use of…

"If you write code in the STL weenie style or the Pretend It's Java style there aren't any idioms I know of that would ever violate the rules he mentions (out-of-range pointers, signed overflow, invalid aliasing)." What does the STL do about signed overflow? As for out of range pointers, that is an easy one to get with the STL: vector somevector(100); somevector[200] = 5; "These are not problems of a language per se"…

I'm not saying the language is some sort of security barrier that prevents any error, I'm saying sanely styled code does not have these issues in practice. The solution is "don't do that, and cultivate habits that will not cause you to do that by accident", not having the compiler make up semantics for broken code or putting in checks everywhere. Just because someone, somewhere does it wrong, doesn't mean it's impossible to do it right.

this:

  vector somevector(100);
  somevector[200] = 5;
Is a C idiom translated by cut-and-paste. The unmotivated poking of arbitrary magic-number offsets into a magic-number sized vector is not proper. It's the kind of thing that sets off alarm bells on even the most casual of review.

Re: C and C++ Aren't Future Proof

#34

I think there is an important point here, which is that C and C++ compilers have let us get away with a lot of undefined behavior for a long time, and that there hasn't been a lot of tooling to help avoid it nor a culture that stresses the long-term danger of depending on it. I can speak as someone who has been programming in C and C++ for over ten years, but only in the last few years became aware of this issue and…

I agree with all your arguments. The function pointer void pointer conversion in particular is an excellent example. But the very last example, unsigned -> signed conversion, is not a good illustration of the point you are making then.

unsigned -> signed conversion is already “implementation-defined behavior” (as opposed to “undefined behavior”). The standard does not guarantee how it behaves but forces compilers to make a choice and to stick to it.

A different example, of a behavior that is really undefined, would be signed arithmetic overflow:

int detect_max(int x) { return x+1 The function above branchlessly detects that its argument is INT_MAX, and returns 1 in this case thanks to 2's complement representation.

Except that it doesn't. The command “gcc -O2” compiles it into “return 0;”. GCC can do this, because signed arithmetic overflow is undefined behavior. The compiler is only taking advantage of undefined behavior in a way locally convenient.

Now that two's complement is (almost) everywhere, making it the standard for signed arithmetic overflows is the sort of bold choice I would like to see, but it won't happen (it would break GCC's existing optimization).

Re: C and C++ Aren't Future Proof

#35
post #13

Earlier quoted context omitted.

I agree that programmers should not take on the burden of supporting hypothetical future compiler optimizations (if that's what you're saying), but this problem could be reduced if compilers started forbidding undefined behavior — then programmers would only have to adapt once.

Much undefined behaviour can't be statically detected, unfortunately.

Is there a way to detect it dynamically, e.g. by running C code under a debug mode or in an interpreter that errors out when undefined behavior is encountered? I've occasionally wanted to have something like that to use in tests, so I could ensure that at least my common code paths aren't relying on undefined behavior. I know about gcc's -ftrapv and a few other options, but nothing comprehensive.

Re: C and C++ Aren't Future Proof

#36
post #25
post #12

Earlier quoted context omitted.

> His suggestion #3, that the standards should define more of the commonly used behavior and leave less of it undefined, wouldn't even require C programmers to do anything about it themselves. I've written Windows, Mac, Linux, Xbox, PlayStation, PSP, iOS, and Android code. The memory model is subtly different for each platform. I just don't think you can define certain behaviour and have that work across disparate pl…

You certainly could define some basic things to make the language safer. For example, make variables always be initialized to zero if not explicitly initialized, and force accessing beyond the bounds of an array to be a fault rather than undefined behavior.

You could, but that comes at a cost. That's why libraries like the STL in C++ provide std::vector::operator[] and std::vector::at() - so the user can freely choose whether to pay the extra cost for the bounds check, or not. That's why C provides both malloc() and calloc() - so the user can freely choose whether memory is zero-initialized, or not.

One of the major design decisions for C/C++ is that you don't pay for what you don't use. This is what makes them so flexible and performant across a wide range of systems and applications, but also leaves these safety choices up to the user. Some languages make that tradeoff, but it's not always the right decision.

Re: C and C++ Aren't Future Proof

#37
post #2

If all people started writing code with more RAII and Smart Pointers this would be a better world. Talking about C, well... it's unsafe by nature, let's face it.

If people treated C++ as a maintenance-only language and used it only for legacy code -- like COBOL -- the world would be a better place. The world would also save billions of dollars. This is not a matter of RAII or "smart" pointers (which are not even smart enough to deal with cyclic references unless the programmer explicitly breaks the cycle). It is a matter of a language whose high-level features are constrained…

[deleted]

Re: C and C++ Aren't Future Proof

#38
post #33

Earlier quoted context omitted.

"If you write code in the STL weenie style or the Pretend It's Java style there aren't any idioms I know of that would ever violate the rules he mentions (out-of-range pointers, signed overflow, invalid aliasing)." What does the STL do about signed overflow? As for out of range pointers, that is an easy one to get with the STL: vector somevector(100); somevector[200] = 5; "These are not problems of a language per se"…

I'm not saying the language is some sort of security barrier that prevents any error, I'm saying sanely styled code does not have these issues in practice. The solution is "don't do that, and cultivate habits that will not cause you to do that by accident", not having the compiler make up semantics for broken code or putting in checks everywhere. Just because someone, somewhere does it wrong, doesn't mean it's imposs…

"I'm saying sanely styled code does not have these issues in practice"

Otherwise known as the "just do it right" argument. This is an argument that goes all the way back to the days of writing everything in assembly language, and it was just as wrong then as it is today. If only a restricted subset of a language can ensure that basic issues do not become serious problems, then the language should be restricted to that subset.

"not having the compiler make up semantics for broken code or putting in checks everywhere"

Really? I would rather have the compiler put in run time checks whenever it cannot infer that no input will cause the program's behavior to be undefined. Thus, the compiler might insert a check here:

  for(i = 0; i 
but not here:

  for(i = 0; i 
nor here:

  if(input.length() > some_vector.length()) {
    throw some_exception();
  }
  for(i = 0; i 
At the very least, requiring bounds checks on array access would create a definition for out-of-bounds pointers: program termination (or perhaps an exception being thrown). A reasonably good compiler can detect when a bounds check is unnecessary and can remote the bounds check as an optimization. Why shouldn't this be something that compilers do -- out-of-bounds array access is never a good thing (oh, wait, you might be dereferencing some arbitrary pointer that you got by some means other than allocating memory with "new" -- OK, fine, but that is what type systems are for; this sort of separation is not unheard of, I see it in Lisp with SBCL's FFI)?

"The unmotivated poking of arbitrary magic-number offsets into a magic-number sized vector is not proper. It's the kind of thing that sets off alarm bells on even the most casual of review."

Perhaps so, but then the answer is not simply "just use the STL." As with most things C++, it requires a long list of things to make code work right, and even people who have been writing C++ code for many years are sometimes surprised to discover that something they thought was fine is actually bad. C++ makes it pretty easy for programmers to do the wrong thing and needlessly difficult to do the right thing, which is why years of expertise are needed to write remotely reliable C++ code.

Re: C and C++ Aren't Future Proof

#39

I think there is an important point here, which is that C and C++ compilers have let us get away with a lot of undefined behavior for a long time, and that there hasn't been a lot of tooling to help avoid it nor a culture that stresses the long-term danger of depending on it. I can speak as someone who has been programming in C and C++ for over ten years, but only in the last few years became aware of this issue and…

I agree with all your arguments. The function pointer void pointer conversion in particular is an excellent example. But the very last example, unsigned -> signed conversion, is not a good illustration of the point you are making then. unsigned -> signed conversion is already “implementation-defined behavior” (as opposed to “undefined behavior”). The standard does not guarantee how it behaves but forces compilers to…

Good point about undefined vs. implementation-defined, though even implementation-defined behavior could break programs that switch to a different implementation that makes a different choice.

Re: C and C++ Aren't Future Proof

#40
post #13

Earlier quoted context omitted.

I agree that programmers should not take on the burden of supporting hypothetical future compiler optimizations (if that's what you're saying), but this problem could be reduced if compilers started forbidding undefined behavior — then programmers would only have to adapt once.

Much undefined behaviour can't be statically detected, unfortunately.

But the the author of the original piece is mostly concerned with undefined behavior that can be detected statically - otherwise, compilers would not be able to exploit it to make optimizations.
Post reply on HN