为一个malloc中的元素分配结构和内存

为一个malloc中的元素分配结构和内存,c,memory-management,malloc,C,Memory Management,Malloc,我确信这是一个基本的问题,但我还没有发现这是否是一个合法的内存分配策略。我从一个文件中读取数据,并填写一个结构。成员的大小在每次读取时都是可变的,所以我的结构元素是这样的指针 struct data_channel{ char *chan_name; char *chan_type; char *chan_units; }; 因此,在阅读之前,我先计算出每个字符串的大小,以便为它们分配内存。我的问题是,我可以在一个malloc中为struct和字符串分配内存,然后填充指

我确信这是一个基本的问题,但我还没有发现这是否是一个合法的内存分配策略。我从一个文件中读取数据,并填写一个结构。成员的大小在每次读取时都是可变的,所以我的结构元素是这样的指针

struct data_channel{
    char *chan_name;
    char *chan_type;
    char *chan_units;
};
因此,在阅读之前,我先计算出每个字符串的大小,以便为它们分配内存。我的问题是,我可以在一个malloc中为struct和字符串分配内存,然后填充指针吗

假设chan_name的大小为9,chan_type为10,chan_units为5。所以我会分配资源,做类似的事情

struct data_channel *chan;

chan = malloc(sizeof(struct data_channel) + 9 + 10 + 5);
chan->chan_name = chan[1];
chan->chan_type = chan->chan_name + 9;
chan->chan_units = chan->chan_type + 10;

因此,我读了几篇关于内存对齐的文章,但我不知道这样做是否是一个问题,或者它可能会产生什么样的意外后果。我已经在我的代码中实现了它,它似乎工作得很好。我只是不想跟踪所有这些指针,因为实际上我的每个结构都有7个元素,我可以有100个以上的通道。这当然意味着700个指针加上每个结构的指针,总共800个。我还必须想出一个办法来解放他们。我还想将此策略应用于字符串数组,然后需要有指向这些字符串的指针数组。我现在没有任何会混合数据类型的结构,这可能是个问题,但我可能是个问题

如果
chan_name
是一个8字符的字符串,
chan_type
是一个9字符的字符串,
chan_units
是一个4字符的字符串,那么当您修复分配给
chan_name
时出现的编译错误时,它可以正常工作


如果为结构和所有字符串(包括它们的字符串终止符)分配足够的内存,那么使用这种方法是可以的。可能不是所有人都推荐,但它会起作用。

我知道当结构末尾有一个数组时,有一种确定的方法可以做到这一点,但由于所有数组都具有相同的类型,您可能会很幸运。确定的方法是:

#包括
#包括
结构StWithArray
{
内布拉布拉赫;
浮动arr[1];
};
结构StWithArray*AllocWithArray(大小\u t nb)
{
size\u t size=nb*sizeof(float)+offsetof(structStWithArray,arr);
返回malloc(大小);
}
在结构中使用实际数组可以保证对齐得到尊重

现在将其应用于您的案例:

#include <stddef.h>
#include <stdlib.h>

struct data_channel
{
    char *chan_name;
    char *chan_type;
    char *chan_units;

    char actualCharArray[1];
};

struct data_channel * AllocDataChannel(size_t nb)
{
    size_t size = nb*sizeof(char) + offsetof(data_channel, actualCharArray);
    return malloc(size);
}
struct data_channel * CreateDataChannel(size_t length1, size_t length2, size_t length3)
{
    struct data_channel * pt = AllocDataChannel(length1 + length2 + length3);
    if(pt != NULL)
    {
        pt->chan_name = &pt->actualCharArray[0];
        pt->chan_type = &pt->actualCharArray[length1];
        pt->chan_name = &pt->actualCharArray[length1+length2];
    }
    return pt;
}
#包括
#包括
结构数据通道
{
char*chan_姓名;
char*chan_型;
char*chan_单位;
字符实际字符[1];
};
结构数据通道*AllocDataChannel(大小\u t nb)
{
size\u t size=nb*sizeof(字符)+offsetof(数据通道,实际字符阵列);
返回malloc(大小);
}
结构数据通道*CreateDataChannel(大小长度1、大小长度2、大小长度3)
{
结构数据\u通道*pt=AllocDataChannel(长度1+长度2+长度3);
如果(pt!=NULL)
{
pt->chan_name=&pt->actualCharArray[0];
pt->chan_type=&pt->actualCharArray[长度1];
pt->chan_name=&pt->actualCharArray[长度1+长度2];
}
返回pt;
}

这部分取决于元素类型。你肯定能用字符串来做这件事;对于其他类型,您必须担心对齐和填充问题

struct data_channel
{
    char *chan_name;
    char *chan_type;
    char *chan_units;
};

struct data_channel *chan;
size_t name_size = 9;
size_t type_size = 10;
size_t unit_size = 5;

chan = malloc(sizeof(struct data_channel) + name_size + type_size + unit_size);
if (chan != 0)
{
    chan->chan_name  = (char *)chan + sizeof(*chan);
    chan->chan_type  = chan->chan_name + name_size;
    chan->chan_units = chan->chan_type + type_size;
}
这在实践中是可行的——在标准化之前已经做了很多年了。我不明白为什么标准会不允许这样做

更棘手的是,如果需要分配一个
int
数组以及两个字符串。然后,您必须担心对齐问题

struct data_info
{
    char *info_name;
    int  *info_freq;
    char *info_unit;
};

size_t name_size = 9;
size_t freq_size = 10;
size_t unit_size = 5;
size_t nbytes = sizeof(struct data_info) + name_size + freq_size * sizeof(int) + unit_size;
struct data_info *info = malloc(nbytes);

if (info != 0)
{
    info->info_freq = (int *)((char *)info + sizeof(*info));
    info->info_name = (char *)info->info_freq + freq_size * sizeof(int);
    info->info_unit = info->info_name + name_size;
}
这采用了一种简单的权宜之计,首先分配最严格对齐的类型(int数组),然后再分配字符串。然而,在这一部分中,您必须对可移植性做出判断。我相信代码在实践中是可移植的

C11有校准设施(
\u Alignof
\u Alignas
,以及
中的
最大校准
),可以改变这个答案(但我还没有充分研究它们,所以我还不确定如何进行),但是这里概述的技术在任何版本的C中都适用,只要您小心数据的对齐

请注意,如果结构中只有一个数组,那么C99提供了一个称为flexible array member(FAM)的旧“struct hack”的替代方案。这允许您将数组显式地作为结构的最后一个元素

    struct data_info
    {
        char *info_name;
        char *info_units;
        int  info_freq[];
    };

    size_t name_size = 9;
    size_t freq_size = 10;
    size_t unit_size = 5;
    size_t nbytes = sizeof(struct data_info) + name_size + freq_size * sizeof(int) + unit_size;
    struct data_info *info = malloc(nbytes);

    if (info != 0)
    {
        info->info_name  = ((char *)info + sizeof(*info) + freq_size * sizeof(int));
        info->info_units = info->info_name + name_size;
    }
注意,本例中没有初始化FAM的步骤,
info\u freq
。不能有多个这样的数组

struct data_channel *chan;

chan = malloc(sizeof(struct data_channel) + 9 + 10 + 5);
chan->chan_name = chan[1];
chan->chan_type = chan->chan_name + 9;
chan->chan_units = chan->chan_type + 10;
注意,所概述的技术不容易应用于结构阵列(至少外部结构阵列)。如果你付出相当大的努力,你就能成功。另外,要注意
realloc()
;如果重新分配空间,则必须在数据移动时修复指针

还有一点:特别是在64位机器上,如果字符串的大小足够一致,那么在结构中分配数组可能比使用指针更好

struct data_channel
{
    char chan_name[16];
    char chan_type[16];
    char chan_units[8];
};

这占用了40个字节。在64位机器上,原始数据结构将占用三个指针的24个字节,再占用(9+10+5)字节的24个字节,总共分配48个字节。

Joachim和Jonathan的答案很好。我想提的唯一补充是这一点

单独的malloc和free为您提供一些基本保护,如缓冲区溢出、访问后恢复 免费等。我指的是基本的,而不是像Valgrind一样的功能。分配一个块并在内部分配将导致此功能丢失

将来,如果malloc完全用于不同的大小,那么单独的malloc可以为您购买来自malloc实现内部不同分配桶的效率