c - 保留指向局部作用域变量的指针

标签 c pointers

第一篇文章,长期读者。我正在学习 C,但拥有多年的 Python 经验。抱歉,如果这是转发,但我什至不知道在谷歌搜索时使用的正确词汇。

我认为我仍然停留在面向对象的思维方式中,这导致我在 C 中遇到愚蠢的事情。我有一个类型“my_type”,其中包含指向第二个类型“my_array”的指针。按照我的 OO 思维方式,我想为 my_type 编写 New 和 Free 函数。在初始化期间,my_type 应该创建一个新的 my_array 并存储它的指针。稍后在销毁期间,my_type 的析构函数应调用 my_array 的析构函数并释放动态分配的内存。

这段代码可以编译,但会产生一个严重的错误

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


/**
* @brief A type implementing a dynamically allocated array
*/
typedef struct my_array {
    int n;
    double *array;
} my_array;


/**
* @brief A type containing a my_array pointer 
*/
typedef struct my_type {
    my_array *a;
} my_type;


/**
* @brief Initialize the dynamically allocated array
*
* @param ma pointer to a my_array
* @param n number of array elements
*/
void my_array_new(my_array *ma, int n) {
    ma->n = n;
    ma->array = (double *) malloc(n * sizeof(double));
}


/**
* @brief Free dynamically allocated memory of a my_array
*
* @param ma pointer to a my_array
*/
void my_array_free(my_array *ma) {
    free(ma->array);
}


/**
* @brief Initialize a my_type
*
* @param mt pointer to a my_type
* @param n number of array elements
*/
void my_type_new(my_type *mt, int n) {
    /* Create a local my_array */
    my_array a;
    my_array_new(&a, n);

    /* Store pointer to a */
    mt->a = &a;

    /* I am holding on to the pointer for a, but does a fall out of scope
     * here? Is this the origin of the error? */
}


/**
* @brief Free allocated my_type
*
* @param mt pointer to my_type
*/
void my_type_free(my_type *mt) {
    my_array_free(mt->a);
}


int main(int argc, char *argv[]) {
    my_type mt;
    int n = 10;

    printf("Initializing my_type %p\n", &mt);
    my_type_new(&mt, n);
    printf("mt.a->n = %d\n", mt.a->n);
    printf("Freeing my_type  %p\n", &mt);
    my_type_free(&mt);

    return 0;
}

这是错误

Initializing my_type 0x7ffede7434c0
mt.a->n = 10
Freeing my_type  0x7ffede7434c0
*** Error in `./test': double free or corruption (out): 0x00007ffede7434c0 ***
======= Backtrace: =========
/lib64/libc.so.6[0x3e5f077d9e]
/lib64/libc.so.6(cfree+0x5b5)[0x3e5f0839f5]
./test[0x400618]
./test[0x400662]
./test[0x4006da]
/lib64/libc.so.6(__libc_start_main+0xf0)[0x3e5f01ffe0]
./test[0x4004f9]

我想我知道这里发生了什么。当我调用 my_type_new 时,我创建了一个本地 my_array 变量。当函数结束时,这超出了范围,但我仍然存储了指针。现在它变得模糊了:正式分配给 mt.a 的内存是否被标记为垃圾,但我仍然可以通过取消引用指针来读出它的值?

my_type_new 创建和存储 my_array 的正确方法是什么?

我愿意接受所有建议,但由于其他原因我不能在这里使用 C++ 类。

最佳答案

这里的问题是 my_type 指向一个在堆栈上分配且不再存在的对象。因此,当您尝试释放它时,您会收到错误,因为 free 函数知道您尝试释放的内存块不是堆上可以释放的 block 。

如果您想以这种方式执行操作,初始化 my_type 实例的函数应该为 my_array 分配空间。然后,除了数组的空间之外,析构函数还应该释放该空间。具体来说:

void my_type_new(my_type *mt, int n) {
  /* Create a local my_array */
  mt->a = malloc(sizeof *mt->a);
  my_array_new(mt->a, n);
}

void my_type_free(my_type *mt) {
  my_array_free(mt->a);
  free(mt->a);
}

理想情况下,您还可以在释放内存块后检查分配足够空间和 NULL 指针的错误。

我还可能提到,您可能会发现最好将这些类型的对象存储在适当的位置。换句话说,如果 my_type 始终需要 my_array 的单个实例,并且您不希望它永远为 null,那么您可以只包含 my_array 实例直接在 my_type 的结构定义中,然后初始化它。这样做的优点是可以减少您定义的对象所需的分配和释放。因此,例如,

typedef struct my_type {
  my_array a;
my_type;

如果您采用这种方法,那么 my_type 的初始化程序和析构函数看起来像这样:

void my_type_new(my_type *mt, int n) {
  my_array_new(&mt->a, n);
}

void my_type_free(my_type *mt) {
  my_array_free(&mt->a);
}

请注意,您不再需要为 my_array 分配额外的空间,因为它直接作为 my_type 的成员包含在内。

需要注意的是,这种面向对象的方法是完全有效的。我更喜欢它,部分原因是它对于不太熟悉 C 的人来说非常熟悉。而且还因为我发现它对于组织数据和功能非常有效。

您可能对 this read 感兴趣。

关于c - 保留指向局部作用域变量的指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31063282/

相关文章:

C - 带有函数内 malloc 的 typedef 结构的动态数组

c - C程序中的数组指针

c - 为什么 getchar 调用 printf 停止工作?

c - 如何裁剪加载 SOIL 的图像

c - 指向结构的指针作为参数,函数内引用的成员产生奇怪的值

pointers - Rust 如何知道哪些类型拥有资源?

混淆 C 指针行为

c - 本地主机 UDP 协议(protocol)丢失数据的可能原因?

c - 为什么要将静态函数替换为类对象宏#define STATIC static?

c - 理解给定的头文件