98

假设我想定义一个表示向量长度及其值的结构:

struct Vector{
    double* x;
    int n;
};

现在,假设我想定义一个向量 y 并为它分配内存。

struct Vector *y = (struct Vector*)malloc(sizeof(struct Vector));

我在互联网上的搜索表明我应该为 x 单独分配内存。

y->x = (double*)malloc(10*sizeof(double));

但是,似乎我为 y->x 分配了两次内存,一次为 y 分配内存,另一次为 y->x 分配内存,这似乎是浪费内存。如果让我知道编译器真正做了什么以及初始化 y 和 y->x 的正确方法是什么,我们将不胜感激。

提前致谢。

4

8 回答 8

176

不,您不会y->x两次分配内存。

相反,您正在为结构(包括指针)分配内存以及指针指向的内容。

这样想:

         1          2
        +-----+    +------+
y------>|  x------>|  *x  |
        |  n  |    +------+
        +-----+

因此,您实际上需要两个分配 (12) 来存储所有内容。

此外,您的类型应该是struct Vector *y因为它是一个指针,并且您永远不应该从mallocC 中转换返回值,因为它可以隐藏您不想隐藏的某些问题 - C 完全能够将void*返回值隐式转换为任何其他指针。

而且,当然,您可能希望封装这些向量的创建,以便更轻松地管理它们,例如:

struct Vector {
    double *data;    // no place for x and n in readable code :-)
    size_t size;
};

struct Vector *newVector (size_t sz) {
    // Try to allocate vector structure.

    struct Vector *retVal = malloc (sizeof (struct Vector));
    if (retVal == NULL)
        return NULL;

    // Try to allocate vector data, free structure if fail.

    retVal->data = malloc (sz * sizeof (double));
    if (retVal->data == NULL) {
        free (retVal);
        return NULL;
    }

    // Set size and return.

    retVal->size = sz;
    return retVal;
}

void delVector (struct Vector *vector) {
    // Can safely assume vector is NULL or fully built.

    if (vector != NULL) {
        free (vector->data);
        free (vector);
    }
}

通过像这样封装创建,您可以确保向量要么完全构建,要么根本不构建——它们不可能半构建。它还允许您在将来完全更改底层数据结构而不影响客户端(例如,如果您想让它们成为稀疏数组以牺牲空间换取速度)。

于 2013-02-08T08:11:54.197 回答
5

第一次,您为 分配内存Vector,这意味着变量x, n

但是x 还没有指出任何有用的东西

这就是为什么也需要第二次分配的原因。

于 2013-02-08T08:11:44.070 回答
4

原则上你已经做对了。对于你想要的,你需要两个malloc()s。

只是一些评论:

struct Vector y = (struct Vector*)malloc(sizeof(struct Vector));
y->x = (double*)malloc(10*sizeof(double));

应该

struct Vector *y = malloc(sizeof *y); /* Note the pointer */
y->x = calloc(10, sizeof *y->x);

在第一行中,您为 Vector 对象分配内存。malloc()返回指向已分配内存的指针,因此 y 必须是 Vector 指针。在第二行中,您为 10 个双精度数组分配内存。

在 C 语言中,您不需要显式强制转换,并且编写sizeof *y而不是sizeof(struct Vector)更利于类型安全,此外,它还节省了键入时间。

你可以重新排列你的结构并像这样做一个malloc()

struct Vector{    
    int n;
    double x[];
};
struct Vector *y = malloc(sizeof *y + 10 * sizeof(double));
于 2013-02-08T08:22:24.230 回答
3

几点

struct Vector y = (struct Vector*)malloc(sizeof(struct Vector));是错的

应该是struct Vector *y = (struct Vector*)malloc(sizeof(struct Vector));因为y持有指向struct Vector.

第一个malloc()只分配足够的内存来保存 Vector 结构(它是指向 double + int 的指针)

2ndmalloc()实际上分配内存来容纳 10 双。

于 2013-02-08T08:14:12.050 回答
1

当您为struct Vector您分配内存时,只需为指针分配内存x,即空间,其中包含地址的值将被放置。因此,您不会为y.x将引用的块分配内存。

于 2013-02-08T08:11:28.080 回答
1

首先 malloc 为 struct 分配内存,包括用于 x 的内存(指向 double 的指针)。第二个 malloc 为 x 指向的 double 值分配内存。

于 2013-02-08T08:11:56.973 回答
1

您实际上可以通过同时分配 Vector 和数组在单个 malloc 中执行此操作。例如:

struct Vector y = (struct Vector*)malloc(sizeof(struct Vector) + 10*sizeof(double));
y->x = (double*)((char*)y + sizeof(struct Vector));
y->n = 10;

这会分配 Vector 'y',然后使 y->x 指向 Vector 结构之后立即分配的额外数据(但在同一内存块中)。

如果需要调整向量的大小,您应该按照建议使用两个分配来进行。然后可以调整内部 y->x 数组的大小,同时保持向量结构 'y' 完整。

于 2013-02-08T08:17:29.783 回答
-1

当您malloc(sizeof(struct_name))自动为结构的完整大小分配内存时,您不需要 malloc 内部的每个元素。

使用-fsanitize=addressflag 检查您如何使用程序内存。

于 2020-11-20T00:26:25.150 回答