指向局部变量的指针仍然存在

时间:2017-11-23 11:11:50

标签: c pointers

我想问一个简单的问题。请考虑附带的代码。在main函数中,通过ctor1或ctor 2以两种不同的方式构建指向结构的指针。在这两种情况下,无论我使用哪种构造函数,程序都可以工作。

ctor1工作的原因是struct实例的内存是在功能框外部(即在堆中)分配的。因此,在ctor1终止后,它将在主函数中可用。

我的问题归结为ctor2功能。据我所知,局部变量“myPtr foo”预计会在函数端被销毁。因此,“那个”指针从现在开始应该指向什么。然而,在执行了该程序之后,我发现两个构造函数都能完美地工作。

显然,有一个微妙的细节让我望而却步。你能解释为什么函数ctor2有效吗? 提前谢谢!

#include <stdio.h>
#include <malloc.h>

int _method(void) {

    return 0;
}//_foo

typedef struct vTable {
    int (*method)(void);
} myPtr;

myPtr *ctor1(void) {
myPtr *foo;

    foo = (myPtr*)malloc(1 * sizeof(myPtr));
    foo->method = &_method;

    return foo;
}//ctor1

void ctor2(myPtr *that) {
myPtr foo = { &_method };

    that = &foo;

    return;
// having reached the function end "foo" is destroyed
// and "that" should point to nothing, supposedly
}//ctor2

int dtor(myPtr *foo) {

    free(foo);
    foo->method = NULL;
    foo = NULL;

    return 0;
}//dtor

int main(void) {
myPtr *vPtr;

    // it works as expected
    vPtr = ctor1();
    printf("%p\n\n", vPtr); // 003E0F68
    dtor(vPtr);

    // it works surprisingly enough
    ctor2(vPtr);
    printf("%p\n", vPtr); // 003E0F68
    printf("%p\n", vPtr); // 003E0F68
    // it keeps on working
    printf("%p\n", vPtr); // 003E0F68
    dtor(vPtr);

    return 0;
}//main

Screenshot

3 个答案:

答案 0 :(得分:1)

代码void ctor2(myPtr *that)that声明为指向myPtr类型的对象的参数。参数按值传递,因此参数that只是传递的内容的副本。更改that不会改变传递的内容。

如果要将指针的值更改为myPtr,则必须将指针传递给指向myPtr的指针:

void ctor2(myPtr **that)

然后你可以用:

改变它
*that = malloc(…);

答案 1 :(得分:1)

这里有几个问题,让我们一个接一个地看看。

首先,在ctor2函数中:

void ctor2(myPtr *that) {
    myPtr foo = { &_method };
    that = &foo;
    return;
}//ctor2

此函数实际上是通过值接收指向myPtr的指针并在本地修改它以指向函数中堆栈上分配的内容。这对传入的指针有效。如果你想修改传入的指针,你会传入一个双指针并取消引用它:

void ctor2(myPtr **that) {
    //malloc foo
    *that = foo;
    return;
}

其次,因为您从未通过调用ctor2来修改vPtr,所以对dtor的第二次调用释放已经释放的内存,这是通常导致崩溃的未定义行为。我很惊讶它没有在你的系统上崩溃,但这是UB的事情,你永远不会知道。

第三,您想要模仿的行为是:

/* constructor */
void Shape_ctor(Shape * const me, int16_t x, int16_t y) {
  static struct ShapeVtbl const vtbl = { /* vtbl of the Shape class */
    &Shape_area_,
    &Shape_draw_
  };
  me->vptr = &vtbl; /* "hook" the vptr to the vtbl */
  me->x = x;
  me->y = y;
}

不同之处在于,在这种情况下,ShapeVtbl结构是静态分配的。这没关系,因为它只指向函数,这些函数不会从对象实例更改为对象实例。但是静态分配允许它在类似的函数中分配并分配给对象。

答案 2 :(得分:0)

为了扩大Eric Postpischil的优秀answer,请考虑,

void foo( int i ) { i++; }

此函数更改传递的值,但该值不是调用时原始文件的副本,

int j=8; 
foo(j);

你不会期望j改变,对吧?

也是如此
void ctor2(myPtr *that) { // my version
    that = NULL;
}

that是在调用时传递的参数的副本,

ctor2(vPtr);

由于vPtr没有变化,您的程序会打印...其值不变。

ctor2可以通过that更改指向的任何值,但对参数本身的任何更改都只会产生局部效果。

您的计划中还有其他错误,正如其他答案所指出的那样。但是为什么ctor2&#34;工作的答案和#34;基本上它没有做任何事情。