0

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

// 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任何指针,但是当函数返回时,不再存在指针,因此指针指向其原始位置。在将指针分配给像 in 这样的指针时,不是这样的情况吗,指针和都指向同一个位置,而不是指向和指向该位置。tptrAllocationtpp = tpkptt

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

4

3 回答 3

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包含的地址ib包含的地址j;自身地址b为0x7FFF59E18538;的地址a未显示在输出中。

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

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

赋值通过参数*z = &q;将地址分配给q指针;它通过改变调用函数中的值来改变指向的地方——这只有在传递了地址的情况下才有可能。bzbbb

赋值p = t;更改了局部变量(其中包含变量inp中的内容的副本),使其指向所指向的内容,即. 因此,'After' 打印语句注意到指向值 10。指向的值在并且仍然是 42;是 的地址。amain()tkp**zq*zq

返回时,代码a中的main()代码不变,因为它的地址没有传递给ptrAllocation(),但b由于它的地址被传递给ptrAllocation()ptrAllocation()修改了指针而发生了变化。


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

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

调用此函数后,传递给它的指针无法可靠地解除引用,因为*p指向的局部变量一旦ptrAllocation()返回就不再有效。k你可以通过 make into来解决这个问题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);
    }
}

请注意,顺便提一下,我留下的代码在每个printf()语句的末尾都有换行符。养成在输出行末尾放置换行符的习惯。如果不这样做,则不知道输出何时会出现(因为它可能会一直保持到下一次生成换行符时)。

于 2013-05-28T02:56:20.123 回答
1

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

如果要让函数更改指针,则必须引用指针,例如通过形成指向指针的指针,并使用按值调用将引用传递给函数。术语“引用”和“指针”在 C 语言中几乎可以互换使用——引用正是指针的作用。

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

于 2013-05-28T02:59:22.123 回答
1

您正在更改函数中的指针,而不是主代码中的指针。因此,只会更改副本。在后一种情况下,您更改指针的位置,以便更改该地址的值。把它当作变量,当你改变副本时,原来的没有任何变化,但是当你改变位置时,变量就改变了。所以你必须通过指针的地址来改变它。

于 2013-05-28T03:00:30.110 回答