【问题标题】:Passing an Array by reference in C在 C 中通过引用传递数组
【发布时间】:2013-11-07 22:00:19
【问题描述】:

我是 C 新手,我有一个疑问。

由于 C 函数创建其参数的本地副本,我想知道为什么以下代码按预期工作:

void function(int array[]){

    array[0] = 4;
    array[1] = 5;
    array[2] = 6;   
}

int main(){

    int array[] = {1,2,3};

    function(array);

    printf("%d %d %d",array[0],array[1],array[2]);

    return 0;
}

线路输出为 4 5 6。

为什么这有效,而以下无效?

void function(int integer){

    integer = 2;
}

int main(){

    int integer = 1;

    function(integer);

    printf("%d",integer);

    return 0;
}

在这种情况下,输出仅为 1。

短版:如果函数作为数组传递,为什么可以修改其父变量的值?

谢谢大家!

【问题讨论】:

  • 任何认为数组是“真正的”指针的人都需要阅读comp.lang.c FAQ 的第 6 节。快速总结:不,他们不是。

标签: c arrays function reference


【解决方案1】:

这是由于数组倾向于衰减为指针。

int a[] = { 1, 2, 3 };
int* p = a; // valid: p is now the address of a[0]
a = p;  // NOT valid.

printf("a = %p\n", a);
printf("p = %p\n", p); // prints same address as a

ap 将打印相同的值。

与其他人所说的相反,a 不是指针,它可以简单地衰减为一个。 http://c-faq.com/aryptr/aryptrequiv.html

在您的第一个function() 中,传递的是数组第一个元素的地址,函数体取消引用该地址。事实上,编译器是这样处理函数原型的:

void function(int* array /*you wrote int array[]*/){
    array[0] = 4;
    array[1] = 5;
    array[2] = 6;   
}

function(&array[0]);

这必须发生,因为您说“未知大小的数组”(int array[])。编译器无法保证推断出按值传递所需的堆栈数量,因此它会衰减为指针。

---- 编辑----

让我们结合您的示例并使用更具特色的名称以使事情更清晰。

#include <stdio.h>

void func1(int dynArray[]) {
    printf("func1: dynArray = %p, &dynArray[0] = %p, dynArray[0] = %d\n",
             dynArray, &dynArray[0], dynArray[0]);
}

void func2(int* intPtr) {
    printf("func2: intPtr = %p, &intPtr[0] = %p, intPtr[0] = %d\n",
             intPtr, &intPtr[0], intPtr[0]);
}

void func3(int intVal) {
    printf("func3: intVal = %d, &intValue = %p\n",
             intVal, &intVal);
}

int main() {
    int mainArray[3] = { 1, 2, 3 };
    int mainInt = 10;

    printf("mainArray = %p, &mainArray[0] = %p, mainArray[0] = %d\n",
             mainArray, &mainArray, mainArray[0]);
    func1(mainArray);
    func2(mainArray);

    printf("mainInt = %d, &mainInt = %p\n",
             mainInt, &mainInt);
    func3(mainInt);

    return 0;
}

ideone 现场演示:http://ideone.com/P8C1f4

mainArray = 0xbf806ad4, &mainArray[0] = 0xbf806ad4, mainArray[0] = 1
func1: dynArray = 0xbf806ad4, &dynArray[0] = 0xbf806ad4, dynArray[0] = 1
func2: intPtr = 0xbf806ad4, &intPtr[0] = 0xbf806ad4, intPtr[0] = 1

mainInt = 10, &mainInt = 0xbf806acc
func3: intVal = 10, &intValue = 0xbf806ad0

func1func2中,“dynArray”和“intPtr”是局部变量,但它们是指针变量,它们从main接收“mainArray”的地址。

此行为特定于数组。如果将数组放入结构体中,则可以按值传递。

【讨论】:

  • 传递的不是数组的地址,而是数组第一个元素的地址。 (相同的内存地址,不同的类型。)
  • 那么,例如,当我们使用 array[1] 时,[1] 是否已经隐式取消引用指针?此外,我尝试将数组初始化为 int array[3] 并在函数头内的 array[3] 中使用,但它仍然打印 4 5 6,即使编译器可以保证传递所需的堆栈数量一切按价值计算。我错过了什么吗?谢谢!
  • @Rogério,给定“int a[]={1,2,3}”,表达式 a[1] 正好等价于 *(a+1)。也就是说,“a”是第一个 int 的地址,“a+1”是紧随其后的 int 的地址,“*(a+1)”是该表达式指向的 int。下标运算符可以被认为是语法糖。至于您的第二点,请参阅我之前的评论。您不能在 C 中按值传递数组。编译器是否有足够的信息来执行此操作并不重要。这不是语言的定义方式。
  • 我稍微改写了答案:由于这个原因,衰减必须发生在动态大小的数组的情况下,但它也发生在其他情况下。还在----编辑处添加了一些额外的解释----。
  • 这是否意味着静态大小的数组将按值传递?还是包装在结构中?
【解决方案2】:

传递给函数的数组被转换为指针。当您将指针作为参数传递给函数时,您只需给出变量在内存中的地址。所以当你修改数组单元格的值时,你编辑的是给函数的地址下的值。

当你将一个简单的整数传递给一个函数时,整数被复制到堆栈中,当你在函数内修改整数时,你修改的是整数的副本,而不是原始整数。

C 中不同类型内存的提醒

在 C 语言中,我们可以使用三种类型的内存:

  • 栈,用于局部变量和函数调用:当我们在main()中创建变量时,我们使用栈来存储变量,当函数被调用时,给方法的参数是在堆栈中的寄存器。当我们退出函数时,我们“弹出”这些参数以返回原始状态,并在函数调用之前使用 used 变量。 (轶事:堆栈溢出是指我们破解堆栈以在函数中使用以前的变量而不将其作为参数传递)
  • 对应于动态分配内存的堆:当我们需要大量数据时,我们使用这个堆,因为堆栈被限制在几兆字节。
  • 存储程序指令的代码

在这个数组由函数传递的情况下,它是一个指针(指向另一个变量的地址),它存储在堆栈中,当我们调用函数时,我们将指针复制到堆栈中。

在整数的情况下,也是存放在栈中的,当我们调用函数的时候,就是拷贝整数。

如果我们要修改整数,可以通过整数的地址来修改指针下的值,像这样:

void function(int *integer)
{
    *integer = 2;
}

int main()
{
    int integer = 1;
    function(&integer);

    printf("%d", integer);

    return 0;
}

【讨论】:

  • 不,数组不是指针。在大多数情况下,数组表达式被隐式转换为指向数组对象第一个元素的指针。
  • “转换为指针” - 错误,可能会让来自 C# 等语言的程序员感到困惑。 C/C++ 中的数组是两件事。它是一块内存,包含 N 个给定类型的实例,以及指向该内存的指针。 ABI 没有提供转发此类数据的机制,因此您只能通过完美匹配您希望传递的参数来实现它——无论是显式的硬编码指纹,还是在 C++ 中为您制作的模板。 int f(char (&amp;arg)[64])template&lt;class T, size_t N&gt; int f(T (&amp;arg)[N]).
【解决方案3】:

在第一个代码中,您传递了指向数组顶部元素的数组地址。因此,当您修改函数中的值并返回主函数时,您仍在访问位于同一地址的同一数组。这称为按引用传递。

但是,在第二种情况下,整数的值从主函数复制到被调用函数。换句话说,这两个整数在内存中的地址不同。所以修改一个不会修改另一个。

【讨论】:

    【解决方案4】:

    数组名是指向数组中第一个元素的指针。在第一个代码示例中,您传递了一个指向包含第一个数组元素的内存位置的指针。在第二个代码示例中,您通过值传递了一个整数,因此它与名为“integer”的局部变量无关

    检查那个链接

    引用传递和值传递

    Pass by Reference / Value in C++

    【讨论】:

    • 在大多数情况下,数组名被转换为指向数组第一个元素的指针。数组名不是指针。
    【解决方案5】:

    '按引用传递'和'按值传递'是有区别的

    引用传递指向内存中的一个位置,值传递直接传递值,数组变量始终是引用,因此它指向内存中的一个位置。整数默认传值

    【讨论】:

      猜你喜欢
      • 2010-11-09
      • 1970-01-01
      • 1970-01-01
      • 2020-07-30
      • 2012-04-17
      • 2019-06-29
      • 2015-07-08
      相关资源
      最近更新 更多