Interestingly, I wasn't even aware the AS/400 did this until I started
reading comp.arch, yet in nearly 10 years of C coding I've never (even
accidentally) written code that would trigger such a trap.
I do signal processing code. Working backwards through data, or in
non-unit strides happens all the time. I expect that *most* of my code,
for the last twenty years would break according to this rule. None of it
is "incorrect" (on the platforms that it runs on). None of it accesses
data outside allocated blocks of memory. It just happens that the
pointer-walking access code leaves the pointer dangling past the
allocated block, after the last access.
This rule essentially means that *p-- is an invalid access mechanism,
unless peculiar care is taken to exit loops early, while *p++ is valid,
*only* because they made a particular exception for that particular case,
because they figured that C compilers on AS/400 systems could afford to
over-allocate all arrays by one byte, so that that last p++ would not
leave the pointer pointing to an "invalid" location. That's a hack, plain
and simple.
I thought the
rule against forming pointers outside an object made sense, and I never
saw any valid reason to do so since the alternative was always cleaner
and more obvious to me.
Explicit index arithmetic, rather than pointer arithmetic, I guess?
See: it's not symmetrical or idempotic after all.
If your pointers are never invalid, you never
have to worry if it's safe to dereference them. Then again, I set
pointers to NULL after every free() too, so you probably consider me
paranoid.
Hey, I do that too. I just don't consider leaving a pointer dangling one
element short of an allocated array to be any less "invalid" than dangling
one element past the end. Or two elements, or some other stride.
This "defect", as you so impolitely call it, is considered by the folks
that use such machines to be a feature, not a bug. Specifically, it is
a feature that _catches_ bugs.
Sure. They write code for banks. Good for them. That machine feature
probably works beautifully in that situation, with the designed-for COBOL
or PL/1 codebase.
I write code that has to waste as few cycles as possible, and take up as
little code space as possible, and be portable to the widest variety of
chips as possible. I don't need to be unrolling the last loops of my
algorithms, just to make sure that the pointers aren't decremented that
one last time.
Personally, I'd love if x86 had some automatic means to catch invalid
pointers on formation instead of catching them on access. Even the
latter isn't very effective, since it only catches pointers that are
outside that page of _any_ valid object; it happily misses accesses not
only outside the original object but also outside of any valid object
but on a valid page.
You know, you can have that, if you want it. There are plenty of people
who build C compilers and run-time environments that put all sorts of
run-time checks into your code. Or: you could use a language (like Java)
that gave you good solid guarantees that you'll get an exception if you
even try to read something out of range. And they don't have pointers as
such to worry about. Peachy keen.
You might consider it "correct" to form invalid pointers, which I'll
grant seems to make a tiny bit of sense if you're used to algorithms
that do that, but if being unable to do that is the price one must pay
to catch invalid accesses, that's a tradeoff I'd make.
It *doesn't* catch invlaid accesses. If anything, some other mechanism
catches invalid accesses. Traping on out-of-range pointer formation just
gets in the way of clean code.
Over-allocating the segment defeats the main purpose of the model:
catching bugs. At best, when your hack does catch a bug, you'll usually
be looking in the wrong place.
You seem to have missed the part of the discussion where over-allocation
was the concession that the AS/400 C implementers gave to the standards
body so that the wildly more common loop-until-just-past-the-end idiom
worked OK. It's *their* hack. Bet their COBOL and PL/1 and Java
compilers don't have to over-allocate like that. They declined to
over-allocate by one element before an array because there's no telling
how large each element might be. That would be too costly. So you end up
with this pallid, asymmetric shadow of the C that might have been (and
once was).