Difference between passing array and array pointer into function in C
Asked Answered
A

3

137

What is the difference between these two functions in C?

void f1(double a[]) {
   //...
}

void f2(double *a) {
   //...
}

If I were to call the functions on a substantially long array, would these two functions behave differently? Would one use more stack space than the other?

Ayannaaycock answered 6/4, 2011 at 21:40 Comment(0)
F
139

First, some standardese:

6.7.5.3 Function declarators (including prototypes)
...
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. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.

So, in short, any function parameter declared as T a[] or T a[N] is treated as though it were declared T *a.

So, why are array parameters treated as though they were declared as pointers? Here's why:

6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

Given the following code:

int main(void)
{
  int arr[10];
  foo(arr);
  ...
}

In the call to foo, the array expression arr isn't an operand of either sizeof or &, so its type is implicitly converted from "10-element array of int" to "pointer to int" according to 6.2.3.1/3. Thus, foo will receive a pointer value, rather than an array value.

Because of 6.7.5.3/7, you can write foo as

void foo(int a[]) // or int a[10]
{
  ...
}

but it will be interpreted as

void foo(int *a)
{
  ...
}

Thus, the two forms are identical.

The last sentence in 6.7.5.3/7 was introduced with C99, and basically means that if you have a parameter declaration like

void foo(int a[static 10])
{
  ...
}

the actual parameter corresponding to a must be an array with at least 10 elements.

Franny answered 6/4, 2011 at 22:29 Comment(1)
There is a difference when using (at least some older) MSVC C++ compilers, due to the compiler incorrectly mangling the function name differently in the two cases (while recognizing that they are the same otherwise), resulting in link problems. See "Won't fix" bug report here connect.microsoft.com/VisualStudio/feedback/details/326874/…Lunarian
M
36

The difference is purely syntaxic. In C, when the array notation is used for a function parameter, it is automatically transformed into a pointer declaration.

Melodeemelodeon answered 6/4, 2011 at 21:42 Comment(2)
@Kaushik: Though they are the same in this case, keep in mind that they are not the same in the general caseCause
@BlueRaja: yes, it is one of the pitfalls of C. The declaration of function parameters is very similar to the declaration of local variables, but there are a few subtle differences (such as this array-to-pointer automatic transform) which are prone to bite the unwary programmer.Melodeemelodeon
N
2

No, there is no difference between them. To test I wrote this C code in Dev C++(mingw) compiler:

#include <stdio.h>

void function(int* array) {
     int a =5;
}

void main() {  
     int array[]={2,4};
     function(array);
     getch();
}

When I disassemble main function in .exe of both calling versions of binary file in IDA I get exactly the same assembly code like below:

push    ebp
mov     ebp, esp
sub     esp, 18h
and     esp, 0FFFFFFF0h
mov     eax, 0
add     eax, 0Fh
add     eax, 0Fh
shr     eax, 4
shl     eax, 4
mov     [ebp+var_C], eax
mov     eax, [ebp+var_C]
call    sub_401730
call    sub_4013D0
mov     [ebp+var_8], 2
mov     [ebp+var_4], 4
lea     eax, [ebp+var_8]
mov     [esp+18h+var_18], eax
call    sub_401290
call    _getch
leave
retn

So there is no difference between the two versions of this call, at least the compiler threats them equally.

Noncontributory answered 6/4, 2011 at 21:54 Comment(1)
Sorry, but this only proves that some version of gcc generates the same assembly on x86 for both. Correct answer, wrong explanation.Descendible

© 2022 - 2024 — McMap. All rights reserved.