C编程:即使在双指针的内容上完成,指针的分配也会丢失

C programming: allocation of pointer lost even when done on the contents of a double pointer

所以,我一直在努力解决这个问题,在深夜费尽心机之后,我决定看看你们是否都想尝试一下。

总而言之,我试图通过将分配代码移交给 main 之外的另一个函数来设置一个动态分配的结构以供在程序中使用。我知道有两种方法可以做到这一点:一种是让函数 return 指向分配的结构的指针,例如:

typeStruct *someFunction()

或者两个,传入一个指向结构的双指针,取消引用它,并在内部指针上分配内存。喜欢:

void someFunction(typeStruct **ptr)

我正在尝试执行后者,因为我想将 return 值用于 char 标志而不是结构本身。

不幸的是,对指针的引用所做的更改似乎没有超出函数的范围。我设法用一个更简单的示例程序复制了我的问题。这是代码:

test.c:

#include "testheader.h"

int main() {

    char success = 0;
    test *t;
    printf("\nBefore setup (in Main), Test: %d", t);
    success = setupTest(&t);
    printf("\nAfter setup (in Main), Test: %d", t);

    free(t->numbers);
    free(t);

    return success;
}

testheader.c:

#include "testheader.h"

const int NUM_NUMBERS = 100;

char setupTest(test **ptr) {
    printf("\nBefore deference, Test: %d", *ptr);
    char flag = 0;
    test *t = *ptr;
    printf("\nBefore malloc, Test: %d", t);
    t = malloc(sizeof(test));
    printf("\nAfter malloc, Test: %d", t);
    if(t != NULL) {
        t->numbers = malloc(sizeof(int) * NUM_NUMBERS);
        if(t->numbers != NULL) {
            for(int i = 0; i < NUM_NUMBERS; ++i) t->numbers[i] = i;
        } else flag = 1;
    } else flag = 1;
    printf("\nEnd of setup, Test: %d", t);
    return flag;
}

testheader.h:

#ifndef TESTHEADER_H
#define TESTHEADER_H

#include <stdlib.h>
#include <stdio.h>
typedef struct test_t {

    int *numbers;

} test;

char setupTest(test **);

#endif

我是这样编译的:gcc test.c testheader.c -o test.exe

我正在使用 gcc 5.1.0 在 git bash 和 Windows 10 的控制台中编译到 运行。

这是 运行ning test.exe 的输出: Console debugging

然后它崩溃了。正如您所看到的,函数内部的行为符合预期,尽管它外部的代码并未反映内部所做的分配,就好像我只是传递了指针本身,而不是使用双指针通过引用传递。

这里有我遗漏的东西吗?

诊断:

线条

test *t = *ptr;
t = malloc(sizeof(test));

只影响局部变量t,不影响**ptr的内容

解决方案:

malloc 在 **ptr 的内容而不是局部变量上,这样分配就不会超出 return 的范围。并交换两条线。

*ptr = malloc(sizeof(test));
test *t = *ptr;
test *t = *ptr;

我不知道你是否认为这是在 tptr 之间建立某种持久的 link(比如C++ 参考),但事实并非如此。它只是将后者分配给前者,当您到达时,这将变得毫无意义:

t = malloc(sizeof(test));

其中 t 被覆盖。

在那之后你缺少的是某种形式的返回 **ptr 的赋值,这意味着它保留了输入时的任何值。当然,这就是您所看到的。

需要做的是获取t的值并用它填充传入的变量:

*ptr = t;

您还可以稍微清理一下您的函数,确保它是原子的——要么一切都应该完成,要么什么都不做 (特别是在内存分配方面)。 Thius 包括对您在地址中传递的指针的任何更改:

int setupTest(test **ptr) {
    // Allocate a test structure. If fail, return indication.

    test *t = malloc(sizeof(test));
    if (t == NULL) {
        return 0;
    }

    // Allocate a numbers sub-structure. If fail,
    // free test structure and return indication.

    t->numbers = malloc(sizeof(int) * NUM_NUMBERS);
    if (t->numbers == NULL) {
        free(t);
        return 0;
    }

    // Have both structures, populate and return success.

    for (int i = 0; i < NUM_NUMBERS; ++i) {
        t->numbers[i] = i;
    }
    *ptr = t;
    return 1;
}

你可以这样考虑:
因为 test *p => p = malloc()
所以 test **p => *p = malloc()

它应该 malloc 到 *ptr 而不是 ptr。

char setupTest(test **ptr) {
    printf("\nBefore deference, Test: %d", *ptr);
    char flag = 0;
    test **t = ptr;
    printf("\nBefore malloc, Test: %d", *t);
    *t = malloc(sizeof(test));
    printf("\nAfter malloc, Test: %d", *t);
    if(t != NULL) {
        (*t)->numbers = malloc(sizeof(int) * NUM_NUMBERS);
        if((*t)->numbers != NULL) {
            for(int i = 0; i < NUM_NUMBERS; ++i) (*t)->numbers[i] = i;
        } else flag = 1;
    } else flag = 1;
    printf("\nEnd of setup, Test: %d", *t);
    return flag;
}

或者,直接 malloc 到 *ptr

char setupTest(test **ptr) {
    printf("\nBefore deference, Test: %d", *ptr);
    char flag = 0;
    printf("\nBefore malloc, Test: %d", *ptr);
    *ptr = malloc(sizeof(test));
    printf("\nAfter malloc, Test: %d", *ptr);
    if(*ptr != NULL) {
        (*ptr)->numbers = malloc(sizeof(int) * NUM_NUMBERS);
        if((*ptr)->numbers != NULL) {
            for(int i = 0; i < NUM_NUMBERS; ++i) (*ptr)->numbers[i] = i;
        } else flag = 1;
    } else flag = 1;
    printf("\nEnd of setup, Test: %d", *ptr);
    return flag;
}