This is plainly not true. Each access to an array would
have two reads from memory + two integers comparisons
to do. Progress in hardware make such tests completely
transparent.
"Progress in hardware" ?? Sounds to me like the old VAX scheme,
where a "pointer" was really a reference to a memory descriptor.
If I recall my history correctly, the people who implemented
the official C for VAX/VMS really tried to adhere to that
architecture, but eventually had to get an operating system
modification to allow the compiled programs to use plain addresses
rather than descriptors.
As best I recall, their plans to use descriptors fell down because of
type punning. For one thing, every malloc'd byte must be accessible
under the definition of malloc, but "objects" get carved up out of
malloc'd space: do you follow the semantics that the bytes are all
accessible because they are part of the malloc'd object, or do you
follow the semantics that the bytes before/after an sub-object
are inaccessible because C says accessing outside of objects is
undefined behaviour? If you have a plain int (for example) and you
pass its address to a subroutine, then *(ptr+1) should trigger
bounds exception processing, but if the space for the int was
part of a malloc'd area then you are really talking about an array
of int's and *(ptr+1) must work [if you stay within the area.]
Even without dynamic memory, it is common (especially in older
C routines that were written to take on mathematical processing
subplanting FORTRAN), to pass in a 1D array but then to use it
internally as a 2D array. Bounds checking based upon the
1D descriptor from when the memory area was created needs to be
flexible enough to handle that kind of type punning, without C
having the semantics to convey to lower level routines how big
something should be. If you do a 1D to 2D punning and then
you pass to a lower routine the address of the first row, intending
only to denote the row, the lower level routine doesn't have
a way to know that an access outside the row but still within
the block should be caught -- as far as the lower level routine
is concerned, you might have wanted to type-pun back to the
entire 1D array. And indeed, I have encountered C code that did
flip back and forth between 1D and 2D at different call depths
(e.g., you might have done a 2D matrix operation and then want
to take the absolute value of all of the elements; taking the
absolute value row-by-row would be less efficient than taking
the absolute element of the entire block as a 1D vector.)
There are languages in which arrays or array slices are "first
class objects", for which hardware bounds checking makes perfect sense.
Unfortunately, C isn't one of those languages, and people *do*
take advantage of the type laxity in real programs; changing the rules
now would have serious issues with backwards compatability.