A
Andrey Tarasevich
Leor said:In the case of function parameters, if an array got passed by value, that
would be a very expensive operation. Most of the time folks would have to
program around it, by writing an expression that evaluates to a pointer of
some kind and passing /that/. But believe it or not, if arrays got passed
by value, that would actually be the special case...
That's not really true. Conceptually, arrays are aggregates and in this
respect they are not different from structs. Making arrays passable "by
value" wouldn't make things more complicated or inconvenient they
already are with structs. Folks would have to do the same thing they
always did - if you want to pass something large and don't need a copy -
pass it "by pointer".
Actually, impossibility to pass arrays by value made things _more_
cumbersome and error prone, because it breaks the invariance of the code
with respect to aggregate types hidden behind typedef-names. The
following function will accepts its parameters very differently,
depending on whether typedef-name 'T' designates array type or struct type
void foo(T agg)
and that's not a good thing.
because of the following:
As I'm fond of saying, arrays are just "smoke and mirrors" anyway; they're
mostly syntactic sugar, and compilers translate array names into pointers
to their first elements (there are a few exceptions, such as when applying
sizeof).
The only reason you can call these contexts (where arrays don't decay
into pointers) "exceptions" is that these contexts are apparently
relatively rare compared to the contexts where arrays do decay to
pointers (especially true for C). But that's a fake reason. Nothing more
than an illusion.
From the formal point of view, arrays keeping their "arrayness" is the
_normal_ behavior of arrays, while arrays decaying into pointers is the
abnormal (exceptional) behavior. It is definitely not correct to say
that arrays are just "smoke and mirrors".
That's true even if you don't pass them to a function.
So, given:
int a[10];
a[3] = 5;
that last line actually compiles into something like:
*(&a[0] + 3) = 5;
Therefore, in a function call such as:
func(a);
it makes perfect sense what gets passed is a pointer to the first element
of the array:
func(&a[0]);
and thus there's no information available to the function about the size of
that array.
Well, it is also important to understand that allowing an array to decay
to a pointer when passed to a function is a useful trick, which has its
own specific purpose. It purpose is to help create functions that can
work with arrays of different sizes.
In contexts where the ability to work with differently sized arrays is
not needed, it makes more sense to pass arrays by array-typed
pointer/reference (i.e. by a pointer/reference of
pointer/reference-to-array type). For example, an application that works
with, say, 3d geometry and used the following type to represent points
in 3D space
typedef int point_t[3];
should pass these points to functions as follows
void foo(const point_t& point)
or
void bar(const int (&point)[3])
not as
void foo(const point_t point)
void bar(const int point[3])
Note, that such function declaration are invariant to the possible
future change of 'point_t' definition to
typedef struct point_t { int x, y, z; };