malloc用于C中的结构和指针


80

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

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的正确方法,将不胜感激。

提前致谢。


5
正如paxdiablo所指出的那样,请不要强制转换malloc()C的返回值。我永远也不会理解为什么每个人都觉得有必要这样做。:(
放松

14
@unwind,也许他们是升级到C的老C ++程序员:-)
paxdiablo

@unwind在C代码上使用Nvidia的nvcc编译器时,如果我不强制转换malloc的结果,则会引发错误。
Nubcake

@Nubcake根据此链接,这可能是因为nvcc的CUDA接口是C ++,所以它以C ++模式运行底层编译器。在C语言中,您不会因此而出错。在C ++void *中,不会自动转换为其他指针,并且需要进行强制类型转换(或者,malloc()当然,不要在C ++中使用)。
放松

@unwind是的,我后来发现了这个问题:)只是想说明一种情况,如果您不强制转换结果,则会抛出错误。
Nubcake

Answers:


151

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

相反,你的结构分配内存(其中包括一个指针)东西该指针指向。

这样想:

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

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

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

而且,当然,您可能希望封装这些向量的创建,以使对它们的管理更加容易,例如:

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);
    }
}

通过像这样封装创建的内容,可以确保向量是完全构建的还是完全不构建的-不可能将它们半构建。它还允许您将来完全更改基础数据结构,而不会影响客户端(例如,如果要使它们稀疏阵列以牺牲空间来提高速度)。


25
我的ASCII艺术技能感到不胜其烦:-)
paxdiablo

非常感谢。真的很有帮助。
普亚

1
在if(retval == NULL)中,retval应该是retVal
Legion Daeth

5

第一次,你分配内存Vector,这意味着变量xn

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

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


3

几点

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

应该是struct Vector *y = (struct Vector*)malloc(sizeof(struct Vector));因为它y拥有指向的指针struct Vector

1stmalloc()仅分配足够的内存来容纳Vector结构(这是double和int的指针)

第2个malloc()实际分配内存以容纳10个双倍。


2

实际上,您可以通过同时分配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;

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

如果需要调整向量的大小,则应按照建议的两个分配进行调整。然后,内部y-> x数组将能够被调整大小,同时保持向量结构'y'不变。


为什么要专门将char键入y?为什么不(double *)y + sizeof(struct Vector)?
Vishnu Prasath 2014年

sizeof返回以字节为单位的结构大小,并且指针算术运算符“ +”将添加到“ y”中,指针是sizeof(y)的倍数如果我们按照您上面的方法进行操作,则y将增加sizeof(double)* sizeof(struct),这太多了。将y转换为char可让我们以sizeof(char)* sizeof(struct)= 1 * sizeof(struct)递增y
PQuinn 2014年

2

原则上,您已经将其正确设置了。对于您想要的东西,您需要两个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));

“节省输入”从来都不是编程决策的有效论据。* y的真正原因是出于安全考虑,请确保为相应变量分配所需的空间。
隆丁

@Lundin我已经更新了答案,但是对我来说,“类型安全”的论点与写作几乎处于同一联盟if(NULL == foo)
Wernsey

你是一个传教的人sizeof *y。如果只为了少输入5个字母(sizeof * y与sizeof(Vector))而没有其他自变量,则一定是因为您发现在键盘上输入5个字母的任务是一个主要障碍。如果是这样,那么也许考虑另一种职业选择,因为编程涉及很多键盘输入……
Lundin

@Lundin写作sizeof *y可以帮助你争取像写入错误sizeof(Vector)时,你的意思sizeof(Matrix)。您多久会犯此类错误?您这样做时发现和修复的速度有多快?我同意这会增加类型安全性,并且我确实用sizeof *y自己的代码编写,但是它几乎与编写if(NULL == foo)以防止在上键入错误一样偏执==
Wernsey

1
@ShmuelKamensky它们在功能上是等效的。y是的指针,struct Vector所以sizeof *y说“ y指向的大小”,所以sizeof struct Vector
韦恩西

0

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


0

首先,malloc为结构分配内存,包括为x(指向double的指针)分配内存。第二个malloc为wtich x指向的双值分配内存。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.