Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

arrays as function arguments

0 views
Skip to first unread message

fka...@googlemail.com

unread,
Feb 22, 2008, 5:58:10 PM2/22/08
to
Hi,

why doesn't calling f1 (see below) cause a warning while calling f2
does? Both seem to pass the wrong type. Is there another way to
declare f1 so that the caller is forced (warned) if the passed array
hasn't the same elements? I hope this is related to C and not a
question of the used compiler (here: gcc-4.1.2).

void f1(int a[4]){}
void f2(int (*b)[4]){}

int main(int argc,char** argv){

int c[4+1];
int (*d)[4+1];

f1(c); /* no warning */
f2(d); /* warning: incompatible pointer type */

return 0;
}

Felix

Ioannis Vranos

unread,
Feb 22, 2008, 6:03:12 PM2/22/08
to


In C, array arguments are equivalent to pointers, so for example:


void somefunc(char array[10])
{}

void somefunc(char array[])
{}

void somefunc(char array[5])
{}

void somefunc(char *array)
{}


are all equivalent.

Default User

unread,
Feb 22, 2008, 6:05:16 PM2/22/08
to
fka...@googlemail.com wrote:

> Hi,
>
> why doesn't calling f1 (see below) cause a warning while calling f2
> does? Both seem to pass the wrong type. Is there another way to
> declare f1 so that the caller is forced (warned) if the passed array
> hasn't the same elements? I hope this is related to C and not a
> question of the used compiler (here: gcc-4.1.2).
>
> void f1(int a[4]){}

In spite of what it may seem, this is identical to:

void f1(int *a);

That size information is completely lost.


Brian

fka...@googlemail.com

unread,
Feb 22, 2008, 6:17:16 PM2/22/08
to
On Feb 23, 12:05 am, "Default User" <defaultuse...@yahoo.com> wrote:
> That size information is completely lost.

O.k., but shouldn't it be lost, too, when calling f2? The compiler
seems to make a difference.

Felix

Richard Tobin

unread,
Feb 22, 2008, 6:38:59 PM2/22/08
to
In article <e4791524-dd5c-4689...@n77g2000hse.googlegroups.com>,
fka...@googlemail.com <fka...@googlemail.com> wrote:

>> That size information is completely lost.

>O.k., but shouldn't it be lost, too, when calling f2? The compiler
>seems to make a difference.

void f2(int (*b)[4]){}

In this case, the argument is not an array (it's a pointer to an
array), so no decay from array to pointer happens.

C's almost-equivalence of arrays and pointers makes some common things
simple, but has occasional counter-intuitive consequences such as this
one.

-- Richard
--
:wq

Ioannis Vranos

unread,
Feb 22, 2008, 6:45:15 PM2/22/08
to

> void f1(int a[4]){}

> void f2(int (*b)[4]){}
>
> int main(int argc,char** argv){
>
> int c[4+1];
> int (*d)[4+1];
>
> f1(c); /* no warning */
> f2(d); /* warning: incompatible pointer type */
>
> return 0;
> }


In the second call you are passing a pointer to an array of 5 elements
in a function taking a pointer to an array of 4 elements. The two
pointer types are different. The "int (*b)[4]" argument type is a
"pointer to an array of 4 ints" and not an "int *", and the int
(*d)[4+1] is a type "pointer to an array of 5 ints" which also is not an
"int *".


On the other hand, built in arrays as arguments are treated like simple
pointer arguments, so

void f1(int a[4]){}

is equivalent to void f1(int *a){}

and


void f1(int a[4][3]){}

is equivalent to

void f1(int **a){}

Default User

unread,
Feb 22, 2008, 6:46:32 PM2/22/08
to
fka...@googlemail.com wrote:

> On Feb 23, 12:05 am, "Default User" <defaultuse...@yahoo.com> wrote:
> > That size information is completely lost.
>
> O.k., but shouldn't it be lost, too, when calling f2?

No, because that was declared as a pointer to an array.

> The compiler
> seems to make a difference.

Really? Which compilers and which differences?

Brian

Default User

unread,
Feb 22, 2008, 6:56:15 PM2/22/08
to
Ioannis Vranos wrote:


> On the other hand, built in arrays as arguments are treated like
> simple pointer arguments, so
>
> void f1(int a[4]){}
>
> is equivalent to void f1(int *a){}

This is correct.

> and
>
>
> void f1(int a[4][3]){}
>
> is equivalent to
>
> void f1(int **a){}

This is NOT correct. The equivalent declaration is

void f1(int (*a)[3]);

Only the leftmost array dimension is "lost" when the array name is
converted to a pointer.

Brian

Andrey Tarasevich

unread,
Feb 22, 2008, 6:56:59 PM2/22/08
to
fka...@googlemail.com wrote:
> Hi,
>
> why doesn't calling f1 (see below) cause a warning while calling f2
> does? Both seem to pass the wrong type. Is there another way to
> declare f1 so that the caller is forced (warned) if the passed array
> hasn't the same elements? I hope this is related to C and not a
> question of the used compiler (here: gcc-4.1.2).
>
> void f1(int a[4]){}

This is equivalent to 'void f1(int* a)'.

> void f2(int (*b)[4]){}
>
> int main(int argc,char** argv){
>
> int c[4+1];
> int (*d)[4+1];
>
> f1(c); /* no warning */

Because the argument type decays from 'int[5]' to 'int*' and that's
exactly what the function expects.

> f2(d); /* warning: incompatible pointer type */

Because the argument type is 'int (*)[5]' and it is not compatible with
the parameter type 'int (*)[4]'. Strictly speaking, the attempt to
convert one to another is ill-formed. By default, in cases like that C
compilers usually limit themselves to mere "warnings" (as opposed to
"errors") for purely historical reasons.

In case of 'f1' there's no reason for the compiler to warn if the actual
array size is different from the "fictive" size specified in the
argument declarator, and there's no way to make it watch for array sizes
in this case.

As a loosely related note: the new C standard (C99) supports the
following declaration syntax

void f2(int a[static 4])

which means that the argument array shall have at least 4 elements. A
quality compiler might actually decide to issue warnings in cases when
objects of array type with less than 4 elements are passed as arguments.
But, of course, there wouldn't be any reason to complain if there are
more than 4 elements in the argument array.

--
Best regards,
Andrey Tarasevich

Ioannis Vranos

unread,
Feb 22, 2008, 7:01:44 PM2/22/08
to
Default User wrote:
>
>> void f1(int a[4][3]){}
>>
>> is equivalent to
>>
>> void f1(int **a){}
>
> This is NOT correct. The equivalent declaration is
>
> void f1(int (*a)[3]);
>
> Only the leftmost array dimension is "lost" when the array name is
> converted to a pointer.


Yes, you are right.

Ioannis Vranos

unread,
Feb 22, 2008, 7:04:22 PM2/22/08
to
Andrey Tarasevich wrote:
>
> As a loosely related note: the new C standard (C99) supports the
> following declaration syntax
>
> void f2(int a[static 4])
>
> which means that the argument array shall have at least 4 elements. A
> quality compiler might actually decide to issue warnings in cases when
> objects of array type with less than 4 elements are passed as arguments.
> But, of course, there wouldn't be any reason to complain if there are
> more than 4 elements in the argument array.


More type safety reason perhaps?

Ben Bacarisse

unread,
Feb 22, 2008, 7:16:19 PM2/22/08
to
"fka...@googlemail.com" <fka...@googlemail.com> writes:

To add a bit of explanation... In

void f2(int (*p)[4]);

the 4 matters. The compiler needs to know how to access p[1][x] and
p[10][x] -- these need the 4 to be part of the type of p.

--
Ben.

karthikbalaguru

unread,
Feb 22, 2008, 10:02:35 PM2/22/08
to
On Feb 23, 3:58 am, "fka...@googlemail.com" <fka...@googlemail.com>
wrote:

They are different pointer types.
int[5] = int*
int (*)[5] != int (*)[4]

Karthik Balaguru

Keith Thompson

unread,
Feb 22, 2008, 11:01:48 PM2/22/08
to

If you haven't already done so, read section 6 of the comp.lang.c FAQ,
<http://www.c-faq.com>.

--
Keith Thompson (The_Other_Keith) <ks...@mib.org>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Antoninus Twink

unread,
Feb 23, 2008, 4:44:10 AM2/23/08
to
On 22 Feb 2008 at 23:46, Default User wrote:

> fka...@googlemail.com wrote:
>> The compiler
>> seems to make a difference.
>
> Really? Which compilers and which differences?

Interesting - the Loser has taken to asking for "OT" information.

Or is something only OT when noone in The Clique is interested in it?

fka...@googlemail.com

unread,
Feb 23, 2008, 6:33:30 AM2/23/08
to
On Feb 23, 1:16 am, Ben Bacarisse <ben.use...@bsb.me.uk> wrote:
> To add a bit of explanation... In
>
> void f2(int (*p)[4]);
>
> the 4 matters. The compiler needs to know how to access p[1][x] and
> p[10][x] -- these need the 4 to be part of the type of p.

I am sorry, I simply don't get your explanation. Why does the 4 matter
to the compiler in this f2 case and not in f1?

With other words: O.k., I've learned that 'int p[4]' and 'int p[5]'
are simply both treated as 'int* p' (f1 case), and the compiler seems
to be satisfied since type checks are o.k. (even though it has lost
information and will not know that accessing 'p[6]' may be beyond what
was passed to f1).

However, besides the fact that 'int(*p)[4]' and 'int(*p)[5]' are
different pointer types, you mean there is another explanation besides
the difference of the pointer types -- could you be more verbose?

By the way: Thanks to all. Your answers were very helpful to me.

Felix

Ben Bacarisse

unread,
Feb 23, 2008, 8:05:44 AM2/23/08
to
"fka...@googlemail.com" <fka...@googlemail.com> writes:

> On Feb 23, 1:16 am, Ben Bacarisse <ben.use...@bsb.me.uk> wrote:
>> To add a bit of explanation... In
>>
>> void f2(int (*p)[4]);
>>
>> the 4 matters. The compiler needs to know how to access p[1][x] and
>> p[10][x] -- these need the 4 to be part of the type of p.
>
> I am sorry, I simply don't get your explanation. Why does the 4 matter
> to the compiler in this f2 case and not in f1?
>
> With other words: O.k., I've learned that 'int p[4]' and 'int p[5]'
> are simply both treated as 'int* p' (f1 case), and the compiler seems
> to be satisfied since type checks are o.k. (even though it has lost
> information and will not know that accessing 'p[6]' may be beyond what
> was passed to f1).

One could speculate on a C+ where the size was part of the type in a
1D array, but historically, that is not what happened. When you
write:

void f(int p[4])
{
p[1] = 42;
}

you get this kind of picture:

+----+ +----+----+----+----+
p: | ---+--->| | 42 | | |
+----+ +----+----+----+----+

because the p is just a pointer variable. To find where to put the
42, the compiler only needs to know the pointer and the size of the
array elements. The "4" would be useful information, but you just
have to accept that that is not the way C chose to go. OK, so far, I
think you know all this.

In the case of

void f(int p[][4])
{
p[1][1] = 42;
}

this is the same as f(int (*p)[4]) -- the "first" array part becomes a
pointer and we could draw it like this:

+----+ +----+----+----+----+----+----+----+----+----+----+----+--
p: | ---+--->| | | | | | 42 | | | | | |
+----+ +----+----+----+----+----+----+----+----+----+----+----+--
\------ p[0] -----/ \------ p[1] -----/ \------ p[2] ----

In order to know where to put the 42, the compiler must, again, know
the size of the array elements. The fact that they are arrays of 4
ints is now crucial. If you passed in a pointer to arrays of 5 ints
(int (*p)[5]) then the whole thing would go wrong. (Of course, this
being C, if you want it to go wrong for some reason, pass that pointer
with a cast and take you chances!)

Does that help?

--
Ben.

Kenny McCormack

unread,
Feb 23, 2008, 8:10:59 AM2/23/08
to
In article <slrnfrvqna...@nospam.invalid>,

Antoninus Twink <nos...@nospam.invalid> wrote:
>On 22 Feb 2008 at 23:46, Default User wrote:
>> fka...@googlemail.com wrote:
>>> The compiler
>>> seems to make a difference.
>>
>> Really? Which compilers and which differences?
>
>Interesting - the Loser has taken to asking for "OT" information.

Well, at least it is better than his ludicrous "TPA"s and the
ever-present "xxx is a troll; we don't talk to trolls; if you talk to a
troll, you won't be accepted into the Clique", which has been the entire
sum content of his posts for the past several years. It *is* an
improvement.

>Or is something only OT when noone in The Clique is interested in it?

Of course. Isn't that obvious?

P.S. Continuing on with the the idea above. It is really funny how
much this ng is controlled by a bunch of geeks who were shunned in high
school. This is their well-deserved revenge.

christian.bau

unread,
Feb 23, 2008, 10:28:38 AM2/23/08
to
fka...@googlemail.com wrote:
> Hi,
>
> why doesn't calling f1 (see below) cause a warning while calling f2
> does? Both seem to pass the wrong type. Is there another way to
> declare f1 so that the caller is forced (warned) if the passed array
> hasn't the same elements? I hope this is related to C and not a
> question of the used compiler (here: gcc-4.1.2).
>
> void f1(int a[4]){}
> void f2(int (*b)[4]){}

There is a rule for function arguments: Whenever you write a function
with an argument declaration that would usually be an array
declaration, then the compiler replaces it from "array of X" to
"pointer to X". So in the first example, usually int a[4] would mean
that a is an array of four ints, so the compiler changes that to "a is
a pointer to int". In the second example, b is a pointer to an array
of four ints. b is not an array, it is a pointer to an array. Because
it is not an array, that rule for changing arrays to pointers doesn't
apply, and b stays a pointer to arrays containing four ints.

This makes a huge difference when you write a+1 or b+1:

a points to ints. a+1 points to the next int, that is it skips one
int.
b points to arrays of four ints. b+1 points to the next array of four
ints, that is it skips one array of four ints, or four ints.

Within these functions, you could print sizeof (*a) and sizeof (*b).
The first will print the size of an int, quite likely 2, 4 or 8. The
second one will print the size of an array of four ints, that is four
times as much.

fka...@googlemail.com

unread,
Feb 24, 2008, 3:27:21 AM2/24/08
to
On Feb 23, 2:05 pm, Ben Bacarisse <ben.use...@bsb.me.uk> wrote:
> +----+ +----+----+----+----+----+----+----+----+----+----+----+--
> p: | ---+--->| | | | | | 42 | | | | | |
> +----+ +----+----+----+----+----+----+----+----+----+----+----+--
> \------ p[0] -----/ \------ p[1] -----/ \------ p[2] ----
>
> In order to know where to put the 42, the compiler must, again, know
> the size of the array elements.

[...]

> Does that help?

Yes! It's clear to me now.
Thanks a lot.
Felix

John Bode

unread,
Feb 26, 2008, 12:54:33 PM2/26/08
to
On Feb 23, 5:33 am, "fka...@googlemail.com" <fka...@googlemail.com>
wrote:

When an array identifier appears in an expression other than as an
operand to sizeof or &, its type is implicitly converted from "N-
element array of T" to "pointer to T", and it's value is set to point
to the first element in the array.

For example, given the array definition

int arr[5];

the type of "arr" is "5-element array of int." When "arr" appears in
an expression, its type is implicitly converted to "pointer to int",
or int *.

This is true of any N-element array of int:

int barr[10];
int carr[20];

Like "arr", whenever "barr" or "carr" appear in an expression, their
types will also be converted to "pointer to int", so they are
effectively interchangeable as function parameters, even though the
array types are all different.

Now let's look at 2-dimensional arrays:

int darr[3][4];
int earr[3][5];

The type of "darr" is "3-element array of 4-element arrays of int."
The type of "earr" is "3-element array of 5-element arrays of int."
When "darr" appears in an expression, its type is converted to
"pointer to 4-element array of int", or int (*)[4]. When "earr"
appears in an expression, its type is converted to "pointer to 5-
element array of int", or int (*)[5].

The pointer types are different, so they are not interchangeable as
function parameters. However, note that the following *would* be
interchangeable:

int farr[4][3];
int garr[5][3];

Both would be converted to the same type (pointer to 3-element array
of int).

0 new messages