【问题标题】:Passing whole array to a function将整个数组传递给函数
【发布时间】:2010-10-22 14:06:58
【问题描述】:

当我们将数组的元素传递给函数时,它被视为普通变量,被调用的函数会创建实际参数的副本并对其进行操作。对形式参数所做的任何更改都不会影响实际参数。

但当我们传递整个数组时,情况并非如此。在这种情况下,它(称为函数)可以访问实际参数,并且对形式参数所做的任何更改都会影响实际参数。为什么会这样?

【问题讨论】:

  • 我相信我对另一个问题stackoverflow.com/questions/3613302/… 的(详细)回答也涵盖了这个问题。基本上这是数组和指针问题之间的区别。
  • 如果不想修改数组内容,就不能用const吗?

标签: c


【解决方案1】:

数组作为指向元素的指针传递。

如果我写:

void doStuff(int *ptr)
{
  ptr[1] = 5;
}

int main()
{
  int arr[] = {0, 1, 2};
  doStuff(arr);
  printf("%d %d %d\n", arr[0], arr[1], arr[2]);
}

输出将是“0 5 2”。那是因为 C 通过值传递实际上是参数的任何内容。参数是一个指向 int 的指针。所以指向 int 的指针是按值传递的。因此,doStuff 在 main 的堆栈帧中获得了指向内存的指针的副本。当它使用ptr[1] 取消引用该指针时,它会跟随指向主内存的指针并在那里修改数组。

C 只能按值传递,但它“浅薄地”这样做。如果你要求它传递一个 int *,它就会传递一个 int *。它只复制指针的值,而不是它指向的任何东西的值。

如果您希望 doStuff 获取自己的数组副本,请按照其他人的建议将数组包装在结构中,或者使用 memcpy 手动深度复制数组,如下所示:

void doStuff(int *ptr, int nElems)
{
  int myCopyOfTheArray[nElems];
  memcpy(myCopyOfTheArray, ptr, sizeof(int) * nElems);
  /* do stuff with the local copy */
}

与使用结构不同,如果 nElems 仅在运行时知道,则 memcpy 方法有效。

【讨论】:

  • 好的,您使用的是什么编译器?只是好奇。
  • 糟糕,该死。如果您指出我不应该释放堆栈数组,那么您是对的!
  • 天哪,我错误地声明了数组!它根本不是有效的 C!抱歉,我要通宵休息,现在是早上 8:30。 :-/
  • @Nyan 旨在向您介绍前两个 cmets。你是对的。
【解决方案2】:

数组不能在 C 中按值传递。它们被降级为指针。所以被调用的函数看到一个指向数组的指针(通过引用传递)并对其进行操作。如果你想复制,你必须明确地这样做,或者将你的数组放在一个结构中(可以通过值传递给函数。)

【讨论】:

    【解决方案3】:

    一般来说,复制整个数组的成本很高。回到最初创建 C 的时代,它很容易耗尽机器资源(尤其是堆栈)。

    如果你真的想传递一个数组,把它包装在一个结构中:

    struct wrap { int array[100]; };
    
    int somefunc(struct wrap large) { ... }
    
    void anotherfunc(void)
    {
        struct wrap a;
        ...add data to array...
        printf("%d\n", somefunc(a));
    }
    

    【讨论】:

      【解决方案4】:

      “按引用传递”是 C 语言中的一个红鲱鱼。函数的所有参数都只能“按值传递”。如果是数组,则以指针的形式传递第一个元素的地址。然后,您可以使用此指针来引用所需的内存位置并读取或写入值。

      【讨论】:

        【解决方案5】:

        我还没有真正看到任何答案涵盖整个问题。据我所知,这个问题看起来像这样:

        给定以下代码:

        int a[5];
        
        foo(a);
        bar(a[0]);
        

        为什么foo 可以操作原始值,而bar 只接收传入值的副本?

        这是因为使用了数组表示法:[] 运算符取消引用它在数组中引用的项并传入该位置的值。

        所以

        int a[5];
        bar(a[0]);
        

        不同于:

        int* a;
        bar(a);
        

        如果要传入对数组中特定项的引用,则需要使用& 运算符:

        bar(&a[5]);
        

        【讨论】:

          【解决方案6】:

          来自online C standard

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

          假设以下代码sn-p:

          int a[10];
          ...
          foo(a);
          

          在对foo 的调用中,表达式 a 的类型是“int 的10 元素数组”。但是,由于表达式不是 sizeof& 运算符的操作数,并且由于它不是用于在声明中初始化另一个数组的字符串文字,因此它的类型被隐式转换(“衰减”)从“int 的 10 元素数组”到“指向int 的指针”,其值将是数组中第一个元素的地址(即&a[0])。

          因此,foo 接收的是指向int 的指针,而不是数组。取消引用或下标此指针允许您更改数组的值。

          这是C语言的一个特性;数组不是一流的对象。事实上,在大多数情况下(包括下标)数组表达式都会被转换为指针类型。

          我一直强调表达式这个词来区分实际的数组object(它永远是一个数组类型)和任何reference到代码中的那个对象,它可以转换为指针类型。

          【讨论】:

            【解决方案7】:

            因为当您将整个数组传递给函数时,您会传递一个指向该数组的指针,这意味着您在内存中为函数提供了该数组所在的位置。因此,当您更改函数中的数组时,您也在更改原始数组。

            【讨论】:

              【解决方案8】:

              C 中的数组可以被视为指向数组第一个元素的指针。指针是按值传递的(不能修改传入的值),但可以取消引用并修改它指向的内存。

              【讨论】:

              • C 中的数组不是指针,它经常被降级为指针。
              猜你喜欢
              • 1970-01-01
              • 2020-07-28
              • 1970-01-01
              • 1970-01-01
              • 2014-12-17
              • 1970-01-01
              相关资源
              最近更新 更多