23

我是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。

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

谢谢你们!

4

5 回答 5

30

这是由于数组倾向于衰减为指针这一事实造成的。

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

a并将p打印相同的值。

与其他人所说的相反,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

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

此行为特定于数组。如果您要将数组放入结构中,那么您将能够按值传递它。

于 2013-10-28T23:02:58.497 回答
3

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

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

提醒 C 中不同类型的内存

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

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

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

在整数的情况下,它也存储在堆栈中,当我们调用函数时,我们复制整数。

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

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

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

    printf("%d", integer);

    return 0;
}
于 2013-10-28T22:59:41.403 回答
1

“按引用传递”和“按值传递”之间存在差异

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

于 2013-10-28T22:55:15.027 回答
1

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

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

于 2013-10-28T23:03:07.433 回答
0

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

检查那个链接

按引用传递和按值传递

在 C++ 中通过引用/值传递

于 2013-10-28T22:58:58.527 回答