Яка різниця між двома функціями в С?
void f1(double a[]) {
//...
}
void f2(double *a) {
//...
}
Якби я викликав функції по суті довгому масиву, чи поводилися б ці дві функції по-різному, чи займали б вони більше місця в стеку?
Яка різниця між двома функціями в С?
void f1(double a[]) {
//...
}
void f2(double *a) {
//...
}
Якби я викликав функції по суті довгому масиву, чи поводилися б ці дві функції по-різному, чи займали б вони більше місця в стеку?
Відповіді:
По-перше, деякі стандарти :
6.7.5.3 Декларатори функцій (включаючи прототипи)
...
7 Декларація параметра як "" масив типу "має бути налаштована на" кваліфікований покажчик на тип ", де кваліфіковані типи (якщо такі є) вказані в межах[
і]
деривації типу масиву. Якщо ключове словоstatic
також відображається в[
і]
виведенні типу масиву, то для кожного виклику функції значення відповідного фактичного аргументу забезпечує доступ до першого елемента масиву з щонайменше стільки ж елементів, скільки визначено розміром вираз.
Отже, коротше кажучи, будь-який параметр функції, оголошений як T a[]
або T a[N]
трактується так, ніби він був оголошений T *a
.
Отже, чому параметри масиву трактуються так, ніби вони були оголошені як покажчики? Ось чому:
6.3.2.1 Значення, масиви та позначення функцій
...
3 За винятком випадків, коли це операндsizeof
оператора чи одинарний&
оператор, або це рядковий літерал, який використовується для ініціалізації масиву, виразу, що має тип '' масив типу ' 'перетворюється у вираз із типом' 'покажчик на тип ' ', який вказує на початковий елемент об’єкта масиву і не є значенням. Якщо об’єкт масиву має клас зберігання в регістрі, поведінка не визначена.
Дано наступний код:
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 елементів.
Різниця суто синтаксична. У C, коли позначення масиву використовується для параметра функції, він автоматично перетворюється на оголошення вказівника.
Ні, різниці між ними немає. Для тестування я написав цей код C у компіляторі Dev C ++ (mingw):
#include <stdio.h>
void function(int* array) {
int a =5;
}
void main() {
int array[]={2,4};
function(array);
getch();
}
Коли я розбираю основну функцію в .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
Тож різниці між двома версіями цього виклику немає, принаймні компілятор загрожує їм однаково.