C语言中结构和指针的malloc

2w3kk1z5  于 2022-12-22  发布在  其他
关注(0)|答案(8)|浏览(152)

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

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的正确方法,我将非常感激。

r1zhe5dt

r1zhe5dt1#

不,您 * 没有 * 为y->x分配内存两次。
相反,您是在为结构(其中包括一个指针)分配内存,* 加上 * 该指针要指向的内容。
你可以这样想:
您实际上需要 * 两个 * 分配(12)来存储所需的一切。
此外,您的类型应该是struct Vector *y,因为它是一个指针,并且您永远不应该在C中强制转换malloc的返回值。
它可以隐藏某些您不想隐藏的问题,并且C完全能够隐式地将void*返回值转换为任何其他指针。
当然,您可能希望封装这些向量的创建过程,以便更容易地管理它们,例如在头文件vector.h中包含以下内容:

struct Vector {
    double *data;    // Use readable names rather than x/n.
    size_t size;
};

struct Vector *newVector(size_t sz);
void delVector(struct Vector *vector);
//void setVectorItem(struct Vector *vector, size_t idx, double val);
//double getVectorItem(struct Vector *vector, size_t idx);

然后,在vector.c中,您可以使用实际的函数来管理向量:

#include "vector.h"

// Atomically allocate a two-layer object. Either both layers
// are allocated or neither is, simplifying memory checking.

struct Vector *newVector(size_t sz) {
    // First, the vector layer.

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

    // Then data layer, freeing vector layer if fail.

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

    // Here, both layers worked. Set size and return.

    vector->size = sz;
    return vector;
}

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

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

通过像这样封装向量管理,您可以确保向量要么完全构建,要么根本不构建--它们不可能构建一半。
它还允许您在将来完全更改底层数据结构而不影响客户端。例如:

  • 如果你想让它们成为稀疏数组以牺牲空间换取速度。
  • 如果您希望数据在每次更改时都保存到持久存储。
  • 如果你想确保所有向量元素都被初始化为零。
  • 如果您希望将矢量大小与矢量容量分开以提高效率(1)。

如果需要,您还可以添加更多功能,例如安全地设置或获取向量值(请参见标题中的注解代码)。
例如,您可以(作为一个选项)静默地忽略有效范围之外的设置值,并在获取这些值时返回零。或者,您可以引发某种描述的错误,或尝试自动展开覆盖下的向量(1)。
在使用向量方面,一个简单的示例类似于以下(非常基本的)main.c

#include "vector.h"

#include <stdio.h>

int main(void) {
    Vector myvec = newVector(42);
    myvec.data[0] = 2.718281828459;
    delVector(myvec);
}

(1)这种可扩展向量的潜力需要进一步解释。
许多向量实现将容量和大小分开,前者是在需要重新分配之前可以使用多少元素,后者是实际的向量大小(总是〈=容量)。
当扩展时,你希望以这样一种方式进行扩展,你不需要做很多,因为这可能是一个昂贵的操作。例如,你可以添加比严格需要的多5%,这样,在一个循环中连续添加一个元素,它不必为每一个单独的项目重新分配。

mrwjdhj3

mrwjdhj32#

第一次,您为Vector分配内存,这意味着变量xn
但是x还没有指向任何有用的东西
这就是为什么还需要第二次分配的原因。

ozxc1zmp

ozxc1zmp3#

原则上你已经做对了,你需要两个malloc()
只是一些评论:

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个double的数组分配内存。
在C语言中,你不需要显式的强制转换,写sizeof *y而不是sizeof(struct Vector)对于类型安全更好,此外,它节省了输入。
你可以重新排列你的结构体,然后像这样执行一个malloc()

struct Vector{    
    int n;
    double x[];
};
struct Vector *y = malloc(sizeof *y + 10 * sizeof(double));
vtwuwzda

vtwuwzda4#

几个点
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的指针)的内存
第二个malloc()实际分配内存以容纳10双精度浮点数。

kkbh8khc

kkbh8khc5#

当你为struct Vector分配内存时,你只为指针x分配内存,也就是说,为空间分配内存,它的值,包含地址,将被放置在那里。所以,这样你就不会为y.x将引用的块分配内存。

xlpyo6sf

xlpyo6sf6#

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

kuarbcqp

kuarbcqp7#

实际上,你可以在一个malloc中同时分配Vector和数组来完成这一操作。

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'不变的情况下调整大小。

krugob8w

krugob8w8#

当你malloc(sizeof(struct_name))时,它会自动为结构体的全部大小分配内存,你不需要malloc里面的每个元素。
使用-fsanitize=address标志检查程序内存的使用情况。

相关问题