Using char[] as function argument is equivalent to have a char* variant


X

Xavier Roche

Hi folks,

I've never really used fixed-size char[] inside function argument list ;
ie. as in:

static void foo(char bar[32]) {
strcpy(bar, "foo");
}

... but I was expecting to get at least a warning when using it as in:

char foo[32];
foo(&foo[1]);

(tested with GCC with -W -Wextra -pedantic)

Aren't the types different ? Or did I obviously miss something
ridiculously trivial ?

My own explanation is that in this case "bar" is NOT of type char[32]
but char* (probably because the array is not copied when calling the
function, but its pointer is) [somehow confirmed by the fact that
sizeof(bar) equals sizeof(char*), and not 32*sizeof(char) ...]

Can a standard specialist confirm this (rather trivial) ?
 
Ad

Advertisements

T

Thomas Richter

Am 11.05.2014 10:57, schrieb Xavier Roche:
static void foo(char bar[32]) {
strcpy(bar, "foo");
}

.. but I was expecting to get at least a warning when using it as in:

char foo[32];
foo(&foo[1]);

(tested with GCC with -W -Wextra -pedantic)

Aren't the types different ?

No. The type of &foo[1] is char *, and so is the function argument of
foo(). Note that arrays decay into pointers when passed to functions,
and formal array arguments of functions are also read as pointer
arguments. IOWs, the above function declaration is identical to

static void foo(char *bar)

with no additional checks for the compiler involved. That you declare it
as char[32] is just a matter of documentation, but has no further
implications as far as the language is concerned.

My own explanation is that in this case "bar" is NOT of type char[32]
but char*

Yes.

Greetings,
Thomas
 
B

BartC

Xavier Roche said:
Hi folks,

I've never really used fixed-size char[] inside function argument list ;
ie. as in:

static void foo(char bar[32]) {
strcpy(bar, "foo");
}

.. but I was expecting to get at least a warning when using it as in:

char foo[32];
foo(&foo[1]);

(tested with GCC with -W -Wextra -pedantic)

Aren't the types different ? Or did I obviously miss something
ridiculously trivial ?

My own explanation is that in this case "bar" is NOT of type char[32]
but char* (probably because the array is not copied when calling the
function, but its pointer is) [somehow confirmed by the fact that
sizeof(bar) equals sizeof(char*), and not 32*sizeof(char) ...]

You can only pass pointers to functions not actual arrays. You can get the
type-check you want using this:

void strfn(char(*s)[32]) { ... }

int main(void) {
char t[33];

strfn(&t);

}

This fails, because a strfn() expects a pointer to char[32], not the pointer
to char[33] that is is passed. But:

* strfn(t) would also fail, because char* is not a pointer to char[32]
either

* As written, you can't index s inside strfn as you'd normally expect (s,
which expects s to be char*); you will have to use (*s), or copy (and
cast) s to a normal char*. Nor can you pass it to standard string functions
as it is.
 
B

Ben Bacarisse

Xavier Roche said:
static void foo(char bar[32]) {
strcpy(bar, "foo");
}

.. but I was expecting to get at least a warning when using it as in:

char foo[32];
foo(&foo[1]);
My own explanation is that in this case "bar" is NOT of type char[32]
but char* (probably because the array is not copied when calling the
function, but its pointer is) [somehow confirmed by the fact that
sizeof(bar) equals sizeof(char*), and not 32*sizeof(char) ...]

Can a standard specialist confirm this (rather trivial) ?

Yes, bar's type is char *. The wording is in "6.7.6.3 Function
declarators (including prototypes)" paragraph 7:

"A declaration of a parameter as 'array of type' shall be adjusted to
'qualified pointer to type', where the type qualifiers (if any) are
those specified within the [ and ] of the array type derivation."

The last bit is a little know feature. It enables you to declare a
const pointer parameter using array notation (though I can't see why one
would do that).
 
G

glen herrmannsfeldt

(snip)
You can only pass pointers to functions not actual arrays.

(snip)

I believe you can pass arrays if they are inside a struct, and
you pass the struct. K&R only allowed passing pointer to struct,
but ANSI added struct by value.

-- glen
 
X

Xavier Roche

Le 11/05/2014 12:01, Ben Bacarisse a écrit :
"A declaration of a parameter as 'array of type' shall be adjusted to
'qualified pointer to type', where the type qualifiers (if any) are
those specified within the [ and ] of the array type derivation."

Ah, great, this is exactly what I was looking for, thank you! The C
standard never fails to surprise me (even in very basic details such as
this one)
 
Ad

Advertisements

C

Charles Richmond

glen herrmannsfeldt said:
(snip)


(snip)

I believe you can pass arrays if they are inside a struct, and
you pass the struct. K&R only allowed passing pointer to struct,
but ANSI added struct by value.

IMHO in this case K&R makes more sense.
 
K

Keith Thompson

Charles Richmond said:
IMHO in this case K&R makes more sense.

Really? Why?

Modern C permits parameters of pointer-to-struct *and* struct types.
K&R1 permitted only the former. How was that better?
 
K

Keith Thompson

Ben Bacarisse said:
Yes, bar's type is char *. The wording is in "6.7.6.3 Function
declarators (including prototypes)" paragraph 7:

"A declaration of a parameter as 'array of type' shall be adjusted to
'qualified pointer to type', where the type qualifiers (if any) are
those specified within the [ and ] of the array type derivation."

The last bit is a little know feature. It enables you to declare a
const pointer parameter using array notation (though I can't see why one
would do that).

I'm not a big fan of defining pointer paramaters using array notation,
but adding "const" does make sense:

void func(const int arr[]) {
/* code that can't modify elements of the array */
}

...

const int arg[42];
func(arg);
 
K

Kenny McCormack

Keith Thompson said:
Modern C permits parameters of pointer-to-struct *and* struct types.
K&R1 permitted only the former. How was that better?

C is intended as a minimalist language. Is there anything you can do with
passing structs that you can't do with pointer-to-struct? [*]

From a practical point-of-view, one can always say that more features are
better, because you are always free to not use the ones you don't like.
But the mentality of C is not like that.

[*] Equivalently, is there anything you could do (that you can't currently do)
if you could pass arrays directly instead of just being able to pass
pointers to them?

(And, yes, I know how these two questions are connected, because you can
now pass arrays [sort of] by wrapping them in a struct)

And, finally, on a personal note, I don't like the passing structs-by-value
thing, because:
a) It's probably not efficient if the struct is large. And it is bad
programming practice (IMHO) in any case.
b) The elegance and beauty of passing structs around by pointer appeals
to me and should not (Again, IMHO) be lost.

--

There are many self-professed Christians who seem to think that because
they believe in Jesus' sacrifice they can reject Jesus' teachings about
how we should treat others. In this country, they show that they reject
Jesus' teachings by voting for Republicans.
 
D

David Brown

Keith Thompson said:
Modern C permits parameters of pointer-to-struct *and* struct types.
K&R1 permitted only the former. How was that better?

C is intended as a minimalist language. Is there anything you can do with
passing structs that you can't do with pointer-to-struct? [*]

From a practical point-of-view, one can always say that more features are
better, because you are always free to not use the ones you don't like.
But the mentality of C is not like that.

[*] Equivalently, is there anything you could do (that you can't currently do)
if you could pass arrays directly instead of just being able to pass
pointers to them?

(And, yes, I know how these two questions are connected, because you can
now pass arrays [sort of] by wrapping them in a struct)

And, finally, on a personal note, I don't like the passing structs-by-value
thing, because:
a) It's probably not efficient if the struct is large. And it is bad
programming practice (IMHO) in any case.
b) The elegance and beauty of passing structs around by pointer appeals
to me and should not (Again, IMHO) be lost.

Passing a struct by value /may/ have optimisation benefits. Of course,
if the struct is large you will need to do a lot more copying. But
small structs can be passed inside registers - using a pointer forces
the compiler to put the struct in memory, and use pointers and memory
access. The other advantage is that when the struct is passed by value,
the compiler knows that it has its own local copy that is private to the
function in question (unless pointers to it are taken and passed out).
If it only has a pointer to the struct, and your function calls other
functions, then the compiler doesn't know whether or not the called
function reads from or writes to the struct.
 
Ad

Advertisements

B

BartC

Kenny McCormack said:
Keith Thompson said:
Modern C permits parameters of pointer-to-struct *and* struct types.
K&R1 permitted only the former. How was that better?

C is intended as a minimalist language. Is there anything you can do with
passing structs that you can't do with pointer-to-struct? [*]
[*] Equivalently, is there anything you could do (that you can't currently
do)
if you could pass arrays directly instead of just being able to pass
pointers to them?
And, finally, on a personal note, I don't like the passing
structs-by-value
thing, because:
a) It's probably not efficient if the struct is large. And it is bad
programming practice (IMHO) in any case.
b) The elegance and beauty of passing structs around by pointer appeals
to me and should not (Again, IMHO) be lost.

Some struct and array types that fit into 32 or 64 bits are passed neatly by
value. It would be silly to use pointers to them, which when such values
need to be returned (more likely with these small types), means managing
memory.

Also, arrays passed by value only really work for specific, fixed size
arrays, which also means the length will be known, or can be deduced, by the
called function.

With larger arrays and structs, again it can reduce the problems of managing
memory, when functions return such arrays and structs. If 'vector' is a
struct (a typedef-ed one):

vector a,b,c,d;

a = addvec(b,addvec(c,d)); // a=b+c+d

Here you don't need to worry about the intermediate result from addvec(c,d);
there's no pointer to memory that needs deallocating.
 
B

Ben Bacarisse

Keith Thompson said:
Ben Bacarisse said:
Yes, bar's type is char *. The wording is in "6.7.6.3 Function
declarators (including prototypes)" paragraph 7:

"A declaration of a parameter as 'array of type' shall be adjusted to
'qualified pointer to type', where the type qualifiers (if any) are
those specified within the [ and ] of the array type derivation."

The last bit is a little know feature. It enables you to declare a
const pointer parameter using array notation (though I can't see why one
would do that).

I'm not a big fan of defining pointer paramaters using array notation,
but adding "const" does make sense:

void func(const int arr[]) {
/* code that can't modify elements of the array */
}

...

const int arg[42];
func(arg);

Sure, but that's not what I commented on -- that's covered by the
adjustment from 'array of type' to 'pointer to type' (the type is simply
const qualified in both cases). I was talking about the rarely used
permission to write

void func(int arr[const]);

This means

void fund(int *const arr);

because the qualifiers apply to the pointer, not to the pointed-to type.
If you *really* want the parameter itself to be const (I never do), then
the "natural" syntax is far less likely to confuse the reader. In fact,
if I wanted a puzzle question about the obscure corners of C syntax,
this would be my first choice.
 
B

Ben Bacarisse

glen herrmannsfeldt said:
(snip)


(snip)

I believe you can pass arrays if they are inside a struct, and
you pass the struct. K&R only allowed passing pointer to struct,
but ANSI added struct by value.

It happened rather before that. K&R even contains the promise that some
of the restrictions on structs will be removed in some future compiler
version. In those days, what the Unix cc command is what C was, so C
acquired passing structs in about 1979 (the same time C got "long").
 
K

Kaz Kylheku

Keith Thompson said:
Modern C permits parameters of pointer-to-struct *and* struct types.
K&R1 permitted only the former. How was that better?

C is intended as a minimalist language. Is there anything you can do with
passing structs that you can't do with pointer-to-struct? [*]

No, and in fact, compilers can implement all struct passing and returning
using hidden arguments that are pointers.

A function that returns a struct can use a calling convention whereby
it receives an argument which indicates where to store it.
From a practical point-of-view, one can always say that more features are
better, because you are always free to not use the ones you don't like.
But the mentality of C is not like that.

This is true, but another aspect of C is that it is a combinative
language, and the combinations of features that can be expressed should
generally work. We don't want to banish "return x" when x happens to be a
struct: we want the feature of value returning to combine with the struct type
feature. (The situation with arrays is unfortunate.)

This is minimalism of another form.

A C language which allows structs to be treated like other values has a smaller
definition than a C language which contains extra rules to prohibit that.
(It just might have a smaller compiler.)

Simiarly, a human being with an amputated leg isn't "more minimal" than one
with two legs. Crippling something is subtly different from minimalism.
 
Ad

Advertisements

M

Malcolm McLean

This is minimalism of another form.

A C language which allows structs to be treated like other values has a smaller
definition than a C language which contains extra rules to prohibit that.
(It just might have a smaller compiler.)

Simiarly, a human being with an amputated leg isn't "more minimal" than one
with two legs. Crippling something is subtly different from minimalism.
We also have rule that every line of C will compile to a handful of machine instructions
which take essentially no time to execute. Allowing entire structures to be copied
in an assignment statement breaks that rule.
 
B

BartC

Malcolm McLean said:
We also have rule that every line of C will compile to a handful of
machine instructions
which take essentially no time to execute. Allowing entire structures to
be copied
in an assignment statement breaks that rule.

How else would you copy structures?

a=b is not going to take any more time than memcpy(&a,&b,sizeof(a)), but
could well be easier to optimise (and is certainly easier to type).

Or would an element-by-element copy be more in keeping with the rules?

When you have a simple expression such as strcmp(p,q) that could potentially
copy all of memory, then the handful of instructions rule goes out the
window.

More important is transparency.
 
M

Malcolm McLean

How else would you copy structures?

a=b is not going to take any more time than memcpy(&a,&b,sizeof(a)), but
could well be easier to optimise (and is certainly easier to type).

More important is transparency.
Let's say a function is in a bottleneck. If the problem is that we've doing a lot of
copying of very large structures, use of memcpy rather than = will make it
somewhat easier to get to the root of the issue.
memcpy should be a special function which the compiler always replaces with
tailored inline code that takes account of alignment and so on. It's not always
the case.
 
Ad

Advertisements

D

David Brown

Richard said:
David Brown said:
On 11/05/14 22:19, Kenny McCormack wrote:
Modern C permits parameters of pointer-to-struct *and* struct types.
K&R1 permitted only the former. How was that better?

C is intended as a minimalist language. Is there anything you can do with
passing structs that you can't do with pointer-to-struct? [*]

From a practical point-of-view, one can always say that more features are
better, because you are always free to not use the ones you don't like.
But the mentality of C is not like that.

[*] Equivalently, is there anything you could do (that you can't currently do)
if you could pass arrays directly instead of just being able to pass
pointers to them?

(And, yes, I know how these two questions are connected, because you can
now pass arrays [sort of] by wrapping them in a struct)

And, finally, on a personal note, I don't like the passing structs-by-value
thing, because:
a) It's probably not efficient if the struct is large. And it is bad
programming practice (IMHO) in any case.
b) The elegance and beauty of passing structs around by pointer appeals
to me and should not (Again, IMHO) be lost.


Passing a struct by value /may/ have optimisation benefits. Of course, if the
struct is large you will need to do a lot more copying. But small structs can
be passed inside registers - using a pointer forces the compiler to put the
struct in memory,

Where it wasn't already?

(I should have noted that you cant KNOW if its been optimised into a
register)

Yes, you can tell if the struct has been put in a register - you look at
the generated object code.

You can also make a good guess in advance - if you are using a
register-poor architecture like the x86, structs will mostly end up in
memory along with everything else. If you are using a register-rich
architecture like most RISC processors, the struct is small, and you
don't take its address, then it will probably go in registers without
having a copy in memory.
 

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

Top