views:

162

answers:

4

Hi. My question is: when i write a function prototype in C like this:

void foo(int *vector);

It's the same thing to do:

void foo(int vector[MAX_LENGTH]);

To the function, is passed always as a pointer? The code it's the same? Thanks in advance.

A: 

there's little else it could pass! the [] contraint lets the compiler do more checks though.

vlabrecque
Most C compilers won't actually do those checks.
Nathon
In fact, any compiler that would try to check the array length against the bound specified in `[]` on a function parameter is non-conformant. The Standard is very clear on this - `[]` just becomes `*` - so something like `void foo(int a[2]) { a[0]++; } ... int x; foo(` has a well-defined meaning, and a compiler that would insert any checks in there is not following the spec.
Pavel Minaev
insert checks? I said _do_ checks. meaning, at compile time!sorry for the confusion
vlabrecque
+10  A: 

Yes an array type is implicitly converted to a pointer type when passed to a function.

So

void foo(int *a) and void foo(int a[]) are identical.

You can easily check that using sizeof() operator inside the function definition

For example

void foo(int a[])
{
   std::cout<<sizeof(a); //prints sizeof(int*)
}

int main()
{

   int a[]={1,2,3,4};
   foo(a);
}

EXTRA (Printing size of an array inside a funtion)

[C++ Only]

 template<typename T,size_t n>
 void size_of_array(T (&a)[n]) //Array passed by reference. Template argument deduction 
 {
    std::cout<<sizeof(a); //prints sizeof(n*sizeof(int))
 }

 int main()
 {
      int a[]={1,2,3,4,5};
      size_of_array(a);
 }
Prasoon Saurav
+1 for pointing out that sizeof is tricksy.
Nathon
For C++, an even better way to demonstrate this is to do `puts(typeid(a).name())`.
Pavel Minaev
+6  A: 

This is subtle. Arrays in C are not pointers, but C does not allow arrays to be passed as function parameters. So when you have void foo(int vector[MAX_LENGTH]);, essentially all you're doing is telling other programmers (and your future self) that this function expects an array of MAX_LENGTH to be passed to it. The compiler won't help you. It will silently cast your array to a pointer.

This explains it pretty well.

Nathon
+1 for making a point on revisiting the code.
Praveen S
+1  A: 

This is one of the rough edges of the C language(s). Two declaration that look exactly the same (but for the names), one in the prototype and one as a stack variable, result in the declaration of two different types of variables.

void foo(int A[10]) {
   int B[10];    
}

Inside the scope of foo, A is pointer to int and B is array of ten elements of type int. As somebody else mentioned, even their sizes computed with sizeof are different.

C++ inherited the rule, so for your example code the prototypes of both functions should be the same.

C99 complicates this matter even further by introducing the new keyword static ;-)

void foo(int A[static 10]) {
   int B[10];    
}

this doesn't change the rules on how A and B are seen from the inside, but provides an information to the caller side of howmuch array elements are expected. For the moment gcc accepts this new syntax and simply ignores this information.

Jens Gustedt
If I remember correctly, the only difference with `static` is that it becomes U.B. for the caller to pass a pointer to an array with fewer elements, even if the function doesn't actually try to dereference them - I guess that allows the compiler to implement it as pass-by-value for small-sized arrays if it sees that array is not mutated in the function, and cannot possibly be mutated through an alias.
Pavel Minaev
@Pavel: I checked the norm and it looks that it is imperative and not U.B., they apply the verbs *shall* and *must*. And in the gcc documentation they remark that this is one of the points where they still are not C99 compliant. Also I don't think that this allows for pass-by-value, since it is allowed to pass pointers to larger arrays than specified.
Jens Gustedt
"If a ‘‘shall’’ or ‘‘shall not’’ requirement that appears outside of a constraint is violated, the behavior is undefined. Undefined behavior is otherwise indicated in this International Standard by the words ‘‘undefined behavior’’ or by the omission of any explicit definition of behavior. There is no difference in emphasis among these three; they all describe ‘‘behavior that is undefined’’."
Pavel Minaev
Pass-by-value can still be done after slicing. Yeah, it's still a stretch... I think the whole point of those restrictions in the Standard is to make it clear that for `a[static n]`, output generated by compiler can access up to `n` first elements of `a`, even if the source code does not. I just can't think of any case, other than attempted pass-by-value, where it would possibly be useful.
Pavel Minaev
Here's the rationale for `static`: http://groups.google.com/group/comp.std.c/browse_thread/thread/2da978d2e99e1a53#66225eb963383b2a"the purpose is to allow the use of data pre-fetch instructions that some DSP and RISC and even some CISC processors now have. The generated code for a function that receives such an array as a parameter can include a pre-fetch instruction to move the contents of the array into a cache or other fast access buffer before they are needed". As well, it serves as a "non-null" constraint, which also allows for some potential optimizations (e.g. `free`).
Pavel Minaev
@Pavel: thanks, great find!
Jens Gustedt