arrays as function arguments

F

fkater

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
 
I

Ioannis Vranos

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


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

Default User

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
 
R

Richard Tobin

That size information is completely lost.
[/QUOTE]
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
 
I

Ioannis Vranos

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 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){}
 
D

Default User

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
 
A

Andrey Tarasevich

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

Ioannis Vranos

Default said:
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.
 
I

Ioannis Vranos

Andrey said:
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?
 
B

Ben Bacarisse

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

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

karthikbalaguru

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;

}

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

Karthik Balaguru
 
K

Keith Thompson

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;
}

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

Antoninus Twink

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?
 
F

fkater

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
 
B

Ben Bacarisse

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?
 
K

Kenny McCormack

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

christian.bau

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

fkater

+----+ +----+----+----+----+----+----+----+----+----+----+----+--
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
 

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,995
Messages
2,570,230
Members
46,818
Latest member
Brigette36

Latest Threads

Top