【问题标题】:Difference between passing array and array pointer into function in C将数组和数组指针传递给C中的函数之间的区别
【发布时间】:2011-07-31 04:58:09
【问题描述】:

C 中这两个函数有什么区别?

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

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

如果我要在一个相当长的数组上调用函数,这两个函数的行为会有所不同,它们会占用更多的堆栈空间吗?

【问题讨论】:

    标签: c arrays function parameter-passing space-efficiency


    【解决方案1】:

    首先,一些standardese

    6.7.5.3 函数声明符(包括原型)
    ...
    7 将参数声明为“type”的数组应调整为“qualified pointer to type'',其中类型限定符(如果有)是在 [] 的 数组类型推导。如果关键字static 也出现在[] 的 数组类型推导,然后每次调用函数,对应的值 实际参数应提供对数组的第一个元素的访问,其中至少有 由 size 表达式指定的元素。

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

    那么,为什么数组参数被视为声明为指针?原因如下:

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

    给定以下代码:

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

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

    【讨论】:

    • 在使用(至少一些较旧的)MSVC C++ 编译器时存在差异,因为编译器在这两种情况下错误地对函数名称进行了不同的处理(同时认识到它们在其他情况下是相同的),导致在链接问题。请参阅此处的“无法修复”错误报告connect.microsoft.com/VisualStudio/feedback/details/326874/…
    【解决方案2】:

    区别纯粹是语法上的。在 C 语言中,当函数参数使用数组表示法时,它会自动转换为指针声明。

    【讨论】:

    • @Kaushik:虽然在这种情况下它们是相同的,但请记住它们并不相同in the general case
    • @BlueRaja: 是的,这是C语言的缺陷之一。函数参数的声明与局部变量的声明非常相似,但有一些微妙之处容易让粗心的程序员陷入困境的差异(例如这种数组到指针的自动转换)。
    【解决方案3】:

    不,它们之间没有区别。为了测试我在 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 中反汇编 ma​​in 函数时,我得到完全相同的汇编代码,如下所示:

    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
    

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

    【讨论】:

    • 对不起,但这仅证明某些版本的 gcc 在 x86 上为两者生成了相同的程序集。正确答案,错误解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-05
    • 2021-12-02
    • 2017-08-09
    • 1970-01-01
    • 2012-07-28
    相关资源
    最近更新 更多