【问题标题】:Arrays & Passing By Reference in CC中的数组和通过引用传递
【发布时间】:2020-10-18 23:16:15
【问题描述】:

我正在学习 C 的一些基础知识,目前正在逐步了解数组,更具体地说,我正在逐步了解按引用传递的工作原理。当下面的代码运行时,它返回 10 22。然而,当我通读代码时,根据最后一个命令,似乎变量 a 应该返回 22 而不是 10(这意味着完整的输出将是 22 22 10 22)。为什么这段代码中的变量 a 不会更新为 22?

#include <stdio.h>

void set_array(int array[4]);
void set_int(int x);

int main(void)
{
    int a = 10;
    int b[4] = { 0, 1, 2, 3 };
    set_int(a);
    set_array(b);
    printf("%d %d\n", a, b[0]);
}

void set_array(int array[4])
{
    array[0] = 22;
}

void set_int(int x)
{
    x = 22;
}

【问题讨论】:

  • C 没有引用。 set_int 采用一个普通的旧值。

标签: c arrays pass-by-reference


【解决方案1】:

数组 [松散地]“通过引用传递”。实际上,数组“衰减”为int *

但是,标量是“按值传递”。

set_int 中,您设置了x 的函数范围副本,但将其返回给调用者。

这是重构的代码,带有“引用调用”示例:

#include <stdio.h>

void
set_array(int array[4])
{
    array[0] = 22;
}

int
set_int(int x)
{
    x = 22;

    return x;
}

void
set_int_byptr(int *x)
{
    *x = 37;
}

int
main(void)
{
    int a = 10;
    int b[4] = { 0, 1, 2, 3 };
    int c = 4;

#if 0
    set_int(a);
#else
    a = set_int(a);
#endif

    set_array(b);
    set_int_byptr(&c);

    printf("a=%d b=%d c=%d\n", a, b[0], c);

    return 0;
}

【讨论】:

  • @snr 虽然它可以完成[稍作改动]:set_array(&amp;a)(需要&amp;,因为a是一个标量 i>,使用带有显式 int * 的单独函数会更清洁 [IMO]。set_array [将来] 可能会扩展为更有用:void set_array(int array[4],int idx) { array[idx] = 22; }。在这种情况下,使用 set_array在标量上会变得混乱
【解决方案2】:

在 C 语言中,如果你想修改传递给函数的变量,你需要将指针传递给它:

例子:

int setval(int *obj, int value)
{
     *obj = val;
     return val;
}

void usage()
{
     int x;
     setval(&x, 22);
}

void *setpointer(void **ptr, size_t size)
{
     *ptr = malloc(size);
     return *ptr;
}

void usage1()
{
    int *array;
    setpointer(&array, 200*sizeof(*array));
}

【讨论】:

    【解决方案3】:

    首先我们需要解决这个问题,因为老实说,我相信它会让事情变得不那么混乱 - C 永远不会通过引用传递 任何 函数参数。 C 按值传递 all 函数参数。有时,这些值是指针。这与传递引用不同

    除其他外,按值传递意味着对形式参数的任何更改都不会反映在实际参数中。在您的set_int 函数中,x 是与a 不同的对象,对x 的任何更改都不会影响a

    如果我们想要一个函数修改参数中的值,我们必须传递一个指向该参数的指针:

    void set_int( int *x )
    {
      *x = 22; // writes a new value to the thing x points to
    }
    
    int main( void )
    {
      int a = 10;
      set_int( &a ); // foo writes a new value to a
      return 0;
    }
    

    在上面的代码中,我们希望函数set_int更新变量a,所以我们必须将一个指向a的指针传递给函数。

     x == &a // int * == int *
    *x ==  a // int   == int
    

    因此,将新值写入set_int 中的表达式 *x 与将新值写入main 中的a 相同。对 x 本身的任何更改都是 set_int 的本地更改。

    当我们将数组添加到组合中时,事情会变得混乱。数组不是指针;但是,除非它是 sizeof 或一元 &amp; 运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,否则为“N 元素数组”类型的 表达式T" 将被转换 ("decay") 为 "pointer to T" 类型的表达式,表达式的值将是第一个元素的地址。

    当您调用set_array(b) 时,表达式 b“衰减”从类型“int”(int [4]) 的类型“指向int 的指针” " (int *),表达式的值与&amp;b[0]相同。

    这是另一个令人困惑的事情 - 在函数参数声明中,T a[N]T a[] 类型的数组声明被解释为 T *a - a 是一个指针 em> 到T,而不是T 的数组。所以你的原型

    void set_array(int b[4])
    

    被解释为

    void set_array(int *b)
    

    它收到的是指向第一个元素的指针。实际上,这意味着set_array 中对array[i] 的任何更改都会反映在b 中,但这是C 专门处理数组表达式的方式 ,参数传递机制没有区别。参数仍然是按值传递的,只是参数是一个指针值,它是对数组表达式进行良好定义的转换操作的结果。

    【讨论】:

      【解决方案4】:

      你在这里做两件事:

      1) 传值: 函数set_int(),它的参数是简单传递的,没有任何地址,就是传值,这个函数set_int()所做的任何改变都不会反映在调用函数中。

      2) 通过引用传递: 但是,在 set_array(b) 的情况下,您将数组传递给被调用函数,并且它的基地址将被传递(表示 b 的第一个元素的地址,即 &b[0]),因此这是传递引用并且对该值所做的任何更改都将反映在调用函数中

      这就是b更新了22,但是a没有更新22的原因

      【讨论】:

      • C 中没有引用传递,只有指针传递
      • @P__J__:某物的地址是对它的引用。传递对象的地址,无论是显式地使用&amp; 还是隐式地使用数组的自动转换,都是通过引用传递的。这个术语在历史上就是这样使用的。事实上,C++ 出现并用它称为“引用”的语言创造了一些东西,这一事实并没有剥夺、否定、取消、废除或使“引用”这个词在其他语言和上下文中的先前使用无效。将数组的名称作为参数写入 C 中的函数调用是通过引用传递。
      • @EricPostpischil 严格来说,C 只能通过值传递。 “按引用传递”只是按值传递指针。所以 C 中的两个术语都是无效的。
      • @P__J__:正如我所解释的,C 确实有引用调用。它只是手动完成,而不是作为自动功能。除了它对数组是自动的这一事实。没有人说 C 没有引用调用,直到 C++ 将类型调用作为引用并且人们开始将 C++ 术语与 C 术语混淆。如果您不想使用该术语,那很好,但没有要求纠正使用它的其他人。他们对这句话的使用是正确的。
      • @EricPostpischil 在 C 标准中,您可以在哪里看到引用调用。 Except for the fact that it is automatic for arrays. 不是通过引用传递的。数组衰减为指针,指针按值传递。
      猜你喜欢
      • 2020-07-30
      • 2012-04-17
      • 2019-06-29
      • 2013-11-07
      • 2010-11-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多