通过引用呼叫的行为

时间:2013-05-28 02:53:54

标签: c pointers pass-by-reference

在我的程序中,我传递一个指向函数的指针。在该函数中,我将传递的指针指向另一个指针所指向的位置。从函数返回时,它不再指向其新位置,而是指向其原始位置。当我通过参考调用时,它应该指向它的新位置。为什么会这样?

// a program to show behavior of call by reference

#include <stdio.h>
#include <stdlib.h>

void ptrAllocation(int *p)
{
    int k = 10 ;
    int *t = &k;
    p = t ;
    printf("\np now points : %d",*p);
}

int main()
{
    int i = 5 ;
    int *a = &i;
    ptrAllocation(a);
    printf("\na now points : %d",*a);
}

输出:

p now points : 10
a now points : 5

我知道如果我创建一个类似的函数可以解决问题:

void ptrAllocation(int **p)
{
    int k = 10 ;
    int *t = &k;
    *p = t ;
    printf("\np now points : %d",**p);
}

但是从指针,位置,堆栈的角度来看,我没有清楚地了解程序中发生的事情?

我的问题: 是指针k指向函数t中的任何指针ptrAllocation,但是当函数返回时,不再存在指针t,因此指针{{1 }指向其原始位置。 是不是在指向p中的指针时,指针p = tp指向同一位置而不是k指向{ {1}}和p指向该位置。

请描述堆栈和指针在上面的程序中是如何工作的。

3 个答案:

答案 0 :(得分:6)

你是通过价值而不是通过参考来传递的。您通过值传递的是指针,但指针仍然按值传递。你可以改变它的意思;你不能改变调用函数中的指针。如果你想这样做,你必须传递一个指向指针的指针,就像你的第二个代码片段一样。

这是一个基于代码的衍生程序,以及Mac OS X 10.8.3上64位构建的输出。我在地址打印中使用12来在这台机器上输出均匀的宽度指针;你可以调整它以适合你的机器。

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

static int q = 42;

static void ptrAllocation(int *p, int **z)
{
    printf("p now points at: %2d (0x%.12" PRIXPTR ")\n", *p, (uintptr_t)p);
    printf("z now points at: %2d (0x%.12" PRIXPTR ") (0x%.12" PRIXPTR ")\n", **z, (uintptr_t)*z, (uintptr_t)z);
    int k = 10;
    int *t = &k;
    *z = &q;
    p = t;
    printf("After:\n");
    printf("p now points at: %2d (0x%.12" PRIXPTR ")\n", *p, (uintptr_t)p);
    printf("z now points at: %2d (0x%.12" PRIXPTR ") (0x%.12" PRIXPTR ")\n", **z, (uintptr_t)*z, (uintptr_t)z);
}

int main(void)
{
    int i = 5;
    int j = 7;
    int *a = &i;
    int *b = &j;
    printf("Before:\n");
    printf("a now points at: %2d (0x%.12" PRIXPTR ")\n", *a, (uintptr_t)a);
    printf("b now points at: %2d (0x%.12" PRIXPTR ")\n", *b, (uintptr_t)b);
    ptrAllocation(a, &b);
    printf("a now points at: %2d (0x%.12" PRIXPTR ")\n", *a, (uintptr_t)a);
    printf("b now points at: %2d (0x%.12" PRIXPTR ")\n", *b, (uintptr_t)b);
}

示例输出:

Before:
a now points at:  5 (0x7FFF59E1852C)
b now points at:  7 (0x7FFF59E18530)
p now points at:  5 (0x7FFF59E1852C)
z now points at:  7 (0x7FFF59E18530) (0x7FFF59E18538)
After:
p now points at: 10 (0x7FFF59E18534)
z now points at: 42 (0x000105DE8050) (0x7FFF59E18538)
a now points at:  5 (0x7FFF59E1852C)
b now points at: 42 (0x000105DE8050)

研究输出应该有助于您更好地了解正在发生的事情。如果需要,您可以打印更多地址值。


  

请描述堆栈和指针在上面的程序中是如何工作的。

我将讨论我展示的程序,因为地址可供讨论。

变量q位于地址0x000105DEE8050。在main()内,变量i存储在堆栈的内存位置0x7FFF59E1852C;变量j存储在内存位置0x7FFF59E18530。变量a包含i的地址; b包含j的地址; b本身的地址是0x7FFF59E18538; a的地址未显示在输出中。

调用ptrAllocation()时,a的值被压入堆栈,b的地址也被压入堆栈。这是一个实现细节,按顺序推送值。

ptrAllocation()内,变量p包含a函数中main()中值的副本。变量z包含b函数中main()的地址。变量k在堆栈中;变量t包含k的地址。

作业*z = &q;通过参数qb的地址指定给指针z;它通过更改调用函数中b的值来更改b指向的内容 - 这是唯一可能的,因为b的地址已通过。

作业p = t;更改了本地变量p(其中包含a中变量main()中的内容的副本),以便它指向t 1}}指向,k。因此,'After'打印语句会指出p指向值10. **z指向的值位于q且仍为42; *zq的地址。

返回时,a代码中的main()未更改,因为其地址未传递给ptrAllocation(),但b已更改,因为其地址已传递给{{ 1}}和ptrAllocation()修改了指针。


正如对该问题的评论所述,您对ptrAllocation()的第二次实施也存在缺陷:

ptrAllocation()

调用此函数后,传递给它的指针无法可靠地解除引用,因为void ptrAllocation(int **p) { int k = 10 ; int *t = &k; *p = t ; printf("\np now points : %d",**p); } 指向一个局部变量,该变量在*p返回后不再有效。您可以通过将ptrAllocation()转换为k,或者通过安排static int k = 10;指向范围超出函数范围的其他*p值来修复此问题 - 定义的变量在函数外部,或动态分配的变量:

int

或者:

void ptrAllocation(int **p)
{
    static int k = 10;
    int *t = &k;
    *p = t ;
    printf("p now points : %d\n", **p);
}

请注意,顺便提一下,我留下的代码在每个void ptrAllocation(int **p) { *p = malloc(sizeof(*t)); if (*p != 0) { **p = 10; printf("p now points : %d\n", **p); } } 语句末尾都有换行符。养成在输出行末端放置换行符的习惯。如果不这样做,则不知道输出何时出现(因为它可能会在下一次生成换行符之前保持不变)。

答案 1 :(得分:1)

C语言只实现按值调用。通过引用调用是指传递(按值)指针p,然后使用*p来操纵它指向的内容( references )。

如果希望函数更改指针,则必须引用指针,例如通过形成指针指针,并使用call by value传递对函数的引用。术语“引用”和“指针”在C中几乎可以互换使用 - 引用就像指针一样。

在C ++中,按引用调用是一种语言特性,但在C语言中它是一种习语。

答案 2 :(得分:1)

您正在更改功能中的指针而不是主代码中的指针。因此,只会更改副本。在后一种情况下,您可以更改指针的位置,以便更改该地址的值。像变量一样,当您更改副本时,原始文件没有任何反应,但是当您更改位置时,变量会更改。所以你必须传递指针的地址来改变它。