使用结构中的指针数组重新分配

时间:2018-01-23 22:33:21

标签: c pointers struct realloc

在C89中,以下代码可以正常工作:

typedef struct{
    int size;
    int **ptr;
}foo;

int main()
{
    foo *var;

    var = malloc(sizeof(foo) + 2 * sizeof(int*));

    int a = 3, b = 6;

    var->ptr[0] = &a;
    var->ptr[1] = &b;

    printf("%d %d\n", (*var->ptr[0]), (*var->ptr[1]));

    return 0;
}

执行时显示:3 6

但如果我更换

var = malloc(sizeof(foo) + 2 * sizeof(int*));

var = malloc(sizeof(foo));
var = realloc(var, sizeof(foo) + 2 * sizeof(int*));

它在没有任何警告的情况下进行编译,但我在以下行中遇到了分段错误:

var->ptr[0] = &a;

我真的不知道我做错了什么,所以我很感激任何输入。我是初学者,所以如果我的错误很明显,我会道歉。

谢谢。

5 个答案:

答案 0 :(得分:5)

我认为您的声明是错误的。以下行假定ptr指向某些内存,而实际上它根本没有初始化!

var->ptr[0] = &a;

如果您的编译器支持它,那么它不应该更像是这样:

typedef struct{
    int size;
    int* ptr[0];
}foo;

否则,请将ptr[0]更改为ptr[1],并根据结构已包含一个元素的事实调整计算。

注意:写入错误的地址后,结果将是未定义的。你有一个问题但它只会出现在一个案例中。在这两种情况下,问题仍然存在。

答案 1 :(得分:2)

我想说这在C89中也是错误的。

malloc(sizeof(foo) + 2 * sizeof(int*));

的分配超出了你的需要,但不是你想象的那样。

+-------+------------+---------+--------+
|  size | ptr        |  int*   | int*   |
+-------+------------+---------+--------+

|  sizeof foo        | 2 * sizeof(int*) |

如您所见,var->foo仍然未初始化,无论多少额外字节 你已经分配了,所以

var->ptr[0] = &a;
var->ptr[1] = &b;

将会失败。

你应该做的是:

var = malloc(sizeof *var);

var->ptr = malloc(2 * sizeof *var->ptr);

var->ptr[0] = &a;
var->ptr[1] = &b;

然后它看起来像这样

                                     | int |     | int |

                                     +-----+     +-----+
                                     |  a  |     |  b  |
                                     +-----+     +-----+
                                         ^         ^
                                         |         |
                                         |         |
+-------+------------+  points to     +---------+--------+
|  size | ptr        | ------------>  | int*    | int*   |
+-------+------------+                +---------+--------+

|  sizeof foo        |                | 2 * sizeof(int*) |

不要忘记检查malloc的返回值并释放内存 之后。

修改

可行的是:

var = malloc(sizeof(foo) + 2 * sizeof(int*));
var->ptr = (int**) ( ((char*) var) + sizeof *var );

您可以将var->pointer设置为指向struct的内存。而 这可能有用,我认为它非常难看,我会拒绝承诺 与此类似的同事。

答案 2 :(得分:1)

问题在于,您没有指定ptr指向任何内容,因此它指向随机内存,ptr[index]的任何访问都是未定义的行为

更改ptr的声明,例如Jonathan建议的答案是最安全的选择,例如:

typedef struct{
    int size;
    int* ptr[1]; // or 0 if your compiler supports it
}foo;

int main()
{
    foo *var;

    var = malloc(offsetof(foo, ptr));
    if (var) {
        var->size = 0;
        ...
    }

    foo *newvar = realloc(var, offsetof(foo, ptr) + (2 * sizeof(int*)));
    if (newvar) {
        var = newvar;
        var->size = 2;

        int a = 3, b = 6;

        var->ptr[0] = &a; // <-- OK!
        var->ptr[1] = &b; // <-- OK!

        printf("%d %d\n", *(var->ptr[0]), *(var->ptr[1]));
    }

    free(var);
    return 0;
}

但是,如果由于某种原因,您无法更改ptr的声明(例如foo与现有API一起使用),那么您可以改为:< / p>

typedef struct{
    int size;
    int **ptr;
}foo;

int main()
{
    foo *var = malloc(sizeof(foo));
    if (var) {
        var->size = 0;
        var->ptr = NULL; // <-- add this!
        ...
    }

    foo *newvar = realloc(var, sizeof(foo) + (2 * sizeof(int*)));
    if (newvar) {
        var = newvar;
        var->size = 2;
        var->ptr = (int**)(var + 1); // <-- add this!

        int a = 3, b = 6;

        var->ptr[0] = &a; // <-- OK!
        var->ptr[1] = &b; // <-- OK!

        printf("%d %d\n", *(var->ptr[0]), *(var->ptr[1]));
    }

    free(var);
    return 0;
}

在后一种情况下,请确保每次要(重新)分配ptr时执行相同的var分配。我会将这个逻辑包装在一组辅助函数中,例如:

typedef struct{
    int size;
    int **ptr;
}foo;

foo* createFoo(int size)
{
    foo *var = malloc(sizeof(foo) + (size * sizeof(int*)));
    if (var) {
        var->size = size;
        var->ptr = (int**)(var + 1);
    }
    return var;
}

foo* resizeFoo(foo **var, int newSize)
{
    foo *newvar = realloc(*var, sizeof(foo) + (newSize * sizeof(int*)));
    if (newvar) {
        newvar->size = newSize;
        newvar->ptr = (int**)(newvar + 1);
        *var = newvar;
    }
    return newvar;
}

int main()
{
    foo *var = createFoo(0);
    ...

    if (resizeFoo(&var, 2)) {
        int a = 3, b = 6;

        var->ptr[0] = &a; // <-- OK!
        var->ptr[1] = &b; // <-- OK!

        printf("%d %d\n", *(var->ptr[0]), *(var->ptr[1]));
    }

    free(var);
    return 0;
}

答案 3 :(得分:0)

您需要修复结构:

typedef struct{
    int size;
    int *ptr;
}foo;

代码:

 var = malloc(sizeof(foo));
 var->ptr = malloc(2 * sizeof(int));

 int a = 3, b = 6;
 // store a and b in array, not their addresses
 var->ptr[0] = a;
 var->ptr[1] = b;

假设你想在数组中输入2个条目。当然,忽略了我没有测试malloc()的结果的事实。

如果您想调整数组大小,只需重新分配var->ptr而不是var

答案 4 :(得分:0)

在Visual Studio,Windows 10中,两个示例都不起作用。这是因为在两种情况下结构都是未初始化的。

箭头操作:

foo->bar

是简写:

(*foo).bar

因此,如果“foo”struct变量未初始化,则取决于编译器,结构中的指针将指向内存中的随机位置。

因此,当您使用箭头运算符时:

var->ptr[0] = &a;

你做的是:

(*var).ptr[0] = &a;

所以,基本上,你试图将“a”的地址写入内存中你不应该访问的位置。这就是您遇到分段错误的原因。

但是,如果你改为初始化它:

int goodmemorylocation = 0;
var->ptr = &goodmemorylocation; // You change the location of your pointer to an accessible location in memory

然后你的代码适用于我的系统中的两种情况。

也许,在你的系统中,你使用malloc的第一个例子,以某种方式指向你内存中的正确地址。这就是它运作的原因。