为一个malloc中的元素分配struct和memory

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

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

所以在阅读之前我弄清楚每个字符串的大小是什么,所以我可以为它们分配内存我的问题是我可以在一个malloc中为结构和字符串分配内存然后填充指针吗?

假设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时的编译错误时它会正常工作。

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

它部分取决于元素类型。 你一定能用字符串做到这一点; 对于其他一些类型,您必须担心对齐和填充问题。

 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有对齐设施( _Alignof_Alignas ,加上 max_align_t )可以改变这个答案(但我没有充分研究它们,所以我不知道怎么样),但是这里列出的技术适用于任何版本的C,只要您注意数据的对齐。

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

  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_freq 。 你不能有这样的多个数组。

注意,所概述的技术不能容易地应用于结构arrays(至少是外部结构的arrays)。 如果你付出相当大的努力,你可以让它发挥作用。 另外,要注意realloc() ; 如果重新分配空间,则必须在数据移动时修复指针。

另一点:特别是在64位机器上,如果字符串的大小足够均匀,你可能会更好地在结构中分配数组,而不是使用指针。

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

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

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

 #include  #include  struct StWithArray { int blahblah; float arr[1]; }; struct StWithArray * AllocWithArray(size_t nb) { size_t size = nb*sizeof(float) + offsetof(structStWithArray, arr); return malloc(size); } 

在结构中使用实际数组可确保对齐。

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

 #include  #include  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; } 

约阿希姆和乔纳森的答案很好。 只有我想提的是这个。

单独的mallocs和frees为你提供一些基本的保护,如缓冲区溢出,免费访问等等。我的意思是基本而不是Valgrind之类的function。 分配一个单块并在内部进行分析将导致此function丢失。

将来,如果mallocs完全针对不同的大小,那么单独的mallocs可能会为您带来来自malloc实现内部不同分配桶的效率,特别是如果您要在不同时间释放它们。

你需要考虑的最后一件事是你多久调用一次mallocs。 如果频繁,那么多个malloc的成本可能很高。