在 C 语言中将数组和数组指针传递到函数的区别

C 语言中这两个函数的区别是什么?

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


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

如果我在一个实质上很长的数组上调用函数,这两个函数的行为是否会有所不同,它们是否会在堆栈上占用更多的空间?

78615 次浏览

这种差异纯粹是语法上的。在 C 语言中,当数组符号用于函数参数时,它会自动转换为指针声明。

不,它们之间没有区别。为了测试,我在 Dev C + + (mingw)编译器中编写了这段 C 代码:

#include <stdio.h>


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


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

中反汇编 总台函数时。在 IDA 中调用二进制文件的两个版本的 exe,我得到了完全相同的汇编代码,如下所示:

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

所以这个调用的两个版本之间没有区别,至少编译器对它们的威胁是一样的。

首先,一些 标准的:

6.7.5.3函数声明器(包括原型)
...
7将参数声明为“ array of 类型”应调整为“修饰指向 类型 ”,其中类型限定符(如果有的话)是在 数组类型派生。如果关键字 static也出现在 数组类型的派生,则对于每个对函数的调用,对应的 实际参数应该提供对数组的第一个元素的访问 大小表达式指定的元素。

因此,简而言之,任何声明为 T a[]T a[N]的函数参数都被处理为 好像,它被声明为 T *a

那么,为什么数组参数被当作指针来对待呢? 原因如下:

6.3.2.1 L 值、数组和函数指示器
...
3除非它是 sizeof操作符或一元 &操作符的操作数,或者是 字符串文字用于初始化数组,类型为“ array of 类型”的表达式为 转换为类型为“指向 类型的指针”的表达式,该表达式指向 数组对象,并且不是左值。如果数组对象具有寄存器存储类,则 行为是未定义的。

根据以下守则:

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

在对 foo的调用中,数组表达式 arr既不是 sizeof的操作数,也不是 &的操作数,因此它的类型根据6.2.3.1/3隐式地从“ int的10元素数组”转换为“指向 int的指针”。因此,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个元素的数组。