Разница между передачей массива и указателем на функцию в C

В чем разница между двумя функциями в C?

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

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

Если бы я должен был вызвать функции на существенно длинном массиве, будут ли эти две функции вести себя по-другому, они занимают больше места в стеке?

Ответ 1

Сначала, standardese:

6.7.5.3 Объявление функций (включая прототипы)
...
7 Объявление параметра как '' массива type должно быть скорректировано на '' квалифицированный указатель на type, где квалификаторы типа (если они есть) - это те, которые указаны в [ и ] тип массива. Если ключевое слово static также появляется в пределах [ и ] тип массива, то для каждого вызова функции значение соответствующего фактический аргумент должен обеспечивать доступ к первому элементу массива, по меньшей мере, столько же элементы, указанные выражением размера.

Таким образом, любой параметр функции, объявленный как T a[] или T a[N], обрабатывается так, как если бы он был объявлен T *a.

Итак, почему параметры массива обрабатываются так, как если бы они были объявлены как указатели? Вот почему:

6.3.2.1 Lvalues, массивы и обозначения функций
...
3 За исключением случаев, когда это операнд оператора sizeof или унарный оператор &, или строковый литерал, используемый для инициализации массива, выражение, которое имеет тип '' массив type, является преобразованный в выражение с типом '' указатель на type, который указывает на начальный элемент объект массива и не является значением lvalue. Если объект массива имеет класс хранения регистров, поведение undefined.

С учетом следующего кода:

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

В вызове foo выражение массива arr не является операндом либо sizeof, либо &, поэтому его тип неявно преобразован из "10-элементного массива int" в "указатель на int" в соответствии с 6.2.3.1/3. Таким образом, foo получит значение указателя, а не значение массива.

Из-за 6.7.5.3/7 вы можете написать foo как

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

но он будет интерпретироваться как

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

Таким образом, две формы идентичны.

Последнее предложение в 6.7.5.3/7 было введено с помощью C99 и в основном означает, что если у вас есть объявление параметра, например

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

фактический параметр, соответствующий a, должен быть массивом с не менее чем 10 элементами.

Ответ 2

Разница чисто синтаксическая. В C, когда обозначение массива используется для параметра функции, оно автоматически преобразуется в объявление указателя.

Ответ 3

Нет, между ними нет разницы. Чтобы проверить, я написал этот код C в компиляторе Dev С++ (mingw):

#include <stdio.h>

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

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

Когда я разбираю функцию main в .exe обеих вызывающих версий двоичного файла в IDA, я получаю точно такой же код сборки, как показано ниже:

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

Таким образом, нет никакой разницы между двумя версиями этого вызова, по крайней мере, компилятор угрожает им одинаково.