Is it ANSI or is it compiler dependent?

  • Thread starter Canonical Latin
  • Start date
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; };
 
C

Canonical Latin

Andrey Tarasevich said:
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; };

I like your reply Andery. But your idea is a bit more revolutionary than
mine. Read my alternative solution of decaying array-to-size-N-of-type-T to
pointer-to-type-T only if there is no matching type for N in the new thread
"Typed Arrays". I won't shy away from some constructive criticism :)
 
L

Leor Zolman

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".

I could have been clearer about what I meant there, but I see your point.
What I meant by it being a special case for arrays to be passed by value,
is that it would be an exception to the rule that an array name 'a' may
have '&a[0]' substituted for it (the current exceptions I know of being
sizeof, & and declarators). So the behavior of arrays as function
parameters is consistent with /that/, not with the behavior of structs.

BTW, I agree in principle that if arrays had always been first-class
citizens, then that would probably have been a Good Thing.
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.


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".

"Not correct"? Okay, so perhaps my view of reality is, in fact, incorrect.
This wouldn't be the first time I've had to come to grips with /that/.
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; };

So that would make boost:;array, or some similar class template, a better
tool for the job in those cases, sure.
-leor
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top