On 11 Jul., 00:54, Joshua Maurice <
[email protected]> wrote:
[snipped discussion about run-time error when OP accessed
uninitialized memory. OP complained that C++ compiler (gcc)
could not detect this even though its warning level was set to
highest]
You seem to be taking the opinion that compilers should
catch all undefined behavior. C++ is not Java. C++'s stated
primary design goals include
- runtime performance comparable with assembly
- don't pay for what you don't use
- portable
- easy to write code / programmer productivity (with less relative
emphasis on this one IMHO)
With these design goals in mind, it is not reasonable to
expect a compiler to catch all possible undefined behavior
or errors. To do that would necessarily restrict the
language so that it's less comparable to assembly in speed
and/or you start paying for things you don't use.
That's not strictly true. Both the C and the C++ standards were
designed so that all undefined behavior can be caught.
Sometimes at a significant price, which means that very few
compilers do so. But there have been some (CenterLine, I
think), and of course, tools like Purify and valgrind catch a
lot (but not all) of the undefined behavior (without rendering
the implementation non-conform).
Just to add my two cents:
1. C++ lets you do everything, so chances are not bad that you
can go beyond your depth. In contrast to this, JAVA restricts
your abilities (no messing around with pointers), which makes
your code inherently safer.
That's provably false. Java seriously restricts what you can
do, to the point of not allowing you to write safe code (for a
sufficiently high enough level of "safe"). Basically, C++
doesn't to anything by default to provide safety, but allows you
(or your organization) to take whatever steps are needed for the
level of safety you need. Java imposes a very specific level of
safety. If it's adequate, fine---you don't have to do anything
else. If it's not, you're stuck, because there's nothing else
you can do. (The specific level Java imposes is NOT adequate
for most of what I do.)
I think both are inferior to programming languages like Ada95.
From what I've heard of it, you're probably right. But I've
never had the occasion to really use it, to be sure.
Ada has a real type system (something that neither C++ nor
JAVA has) and will perform zounds of checks (it is the only
language I know that handles integer overflows).
Again, C++ leaves behavior in case of overflow of signed
integral types or floating point types "undefined behavior". So
an implementation can perform all of the checks it wants. The
problem is that most implementations defined the behavior much
like Java does, which is useless (at least for "safe" software).
And the real problem is that most programmers accept such
implementations, and consider them normal---that most
programmers don't care about safety. (I've written C code in
the past which verified integral overflow, and I could do it in
Java or C++. But such code will never be as efficient as if the
compiler did it.)
Since these checks give you a lot of performance penalties,
Are you sure of that. I seem to recall reading that in typical
programs, a decent compiler is able to eliminate 90% of the
checks entirely. And if the compiler is generating the code,
it's one extra instruction per operation for the checks which
cannot be eliminated (at least on the machines I'm familiar
with). Not a killer for most applications.
you have to provide additional information about which checks
can be omitted. This is maybe the major difference between C++
and Ada95: Out of the box C++ provides few checks in favor of
speed, whereas Ada95 has all checks turned on. So C++ you have
to OPT-IN for run-time checks, Ada95 has the converse OPT-OUT
philosophy. Needless to say, nobody uses Ada95 except the
Bundeswehr in Germany (AFAIK).
Most C++ compilers don't allow you to opt-in, even though it's
the only reasonable option for most software.
2. Maybe even such fancy languages like Ada cannot reliably
detect memory aliasing issues because it may be the case that
this task is Turing hard.
I'm not sure which aliasing issues you're concerned about, but a
lot of languages I've seen used in the past don't allow you to
take the address of a variable (so pointers can only come from
dynamic allocation), use garbage collection (so a pointer can
never point to a non-allocated object---or worse, memory that
has since been allocated to a different object), and don't
support pointer arithmetic, so pointers can't point into the
middle of objects. Under such conditions, aliasing isn't
a difficult problem.
I haven't had time to think about it in detail, but I think
that you could reduce the HALTING problem to the problem of
accessing uninitialized memory through aliasing. This would
explain why the compiler industry didn't come up with a
"decent" compiler: It just may be that detecting _ALL_ such
errors is simply impossible (which doesn't mean that there may
be a good heuristic algorithm for detecting most of the
obvious bugs). I further assume that most cases where you get
UB are also due to the impossibiliy to check for such cases
algorithmically.
Compile time or runtime. The C++ standard certainly allows
"fat" pointers, which contain enough information for the runtime
to be able to detect all undefined behavior. Such an
implementation would run slower; an even greater problem is that
it wouldn't be compatible with the defined ABI of most
platforms.