通过引用呼叫的行为

Sub*_*bbu 0 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);
}
Run Code Online (Sandbox Code Playgroud)

输出:

p now points : 10
a now points : 5
Run Code Online (Sandbox Code Playgroud)

我知道如果我做一个像下面这样的函数可以解决问题:

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

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

我的问题: 为指针已k指向任何指针t在功能上分ptrAllocation,但作为函数返回时,也不再存在的指针t,因此指针p指向原来的位置.难道不是这样的,像在分配指针的指针时p = t,两个指针pk指向相同的位置,而不是那ptt点的位置.

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

Jon*_*ler 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);
}
Run Code Online (Sandbox Code Playgroud)

样本输出:

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)
Run Code Online (Sandbox Code Playgroud)

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


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

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

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

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

里面ptrAllocation(),变量p包含了值的副本amain()功能.变量z包含的地址bmain()功能.变量k在堆栈上; 变量t包含地址k.

赋值通过参数为指针*z = &q;分配地址; 它通过改变调用函数的值来改变指向的东西- 这是唯一可能的,因为传递了地址.qbzbbb

分配p = t;改变了局部变量p(其中包含的是什么变量的副本amain(),使其指向什么)t指着,这是k.因此,'After'打印语句会注意到p值为10的**z点.指向的值在q且仍为42; *z是的地址q.

在返回时,amain()代码不变,因为其地址没有被传递到ptrAllocation(),但b被改变,因为它的地址传递给ptrAllocation()ptrAllocation()修改的指针.


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

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

调用此函数后,传递给它的指针无法可靠地解除引用,因为*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);
}
Run Code Online (Sandbox Code Playgroud)

要么:

void ptrAllocation(int **p)
{
    *p = malloc(sizeof(*t));
    if (*p != 0)
    {
        **p = 10;
        printf("p now points : %d\n", **p);
    }
}
Run Code Online (Sandbox Code Playgroud)

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