我确信这是一个基本问题,但我无法确定这是否是合法的内存分配策略。我正在读取文件中的数据,我正在填写一个结构。成员的大小在每次读取时都是可变的,所以我的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个。我还必须设计一种方法来释放它们。我还想将这个策略应用于字符串数组,然后我需要有一个指针数组。我现在没有任何结构混合数据类型可能是一个问题,但我可能会成为一个问题?
答案 0 :(得分:5)
如果chan_name
是一个8个字符的字符串,chan_type
是一个9个字符的字符串,而chan_units
是一个4个字符的字符串,那么是的,当您修复编译错误时它会正常工作在分配给chan_name
时有。
如果为结构分配了足够的内存以及所有字符串(包括它们的字符串终结符),那么可以使用这样的方法。也许并非所有人都推荐,但它会起作用。
答案 1 :(得分:3)
部分取决于元素类型。你一定能用字符串做到这一点;对于其他一些类型,您必须担心对齐和填充问题。
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
以及<stdalign.h>
加上max_align_t
中的<stddef.h>
)可能会改变这个答案(但我没有对它们进行了充分的研究,所以我不确定如何,但是这里列出的技术可以在任何版本的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
。你不能有这样的多个数组。
注意,所概述的技术不能容易地应用于结构阵列(至少是外部结构的阵列)。如果你付出相当大的努力,你可以让它发挥作用。另外,要小心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个字节。
答案 2 :(得分:1)
我知道当你在一个结构的末尾有一个数组时,有一种可靠的方法可以做到这一点,但由于你的所有数组都有相同的类型,你可能会很幸运。确定的方法是:
#include <stddef.h>
#include <stdlib.h>
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 <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;
}
答案 3 :(得分:0)
Joachim和Jonathan的答案很好。我只想提一下这个。
单独的mallocs和frees为你提供一些基本的保护,比如缓冲区溢出,访问之后 免费,等等。我的意思是基本而不是Valgrind的功能。分配一个单块并在内部进行分配将导致此功能丢失。
将来,如果mallocs完全针对不同的大小,那么单独的malloc可能会为你带来来自malloc实现内部不同分配桶的效率,特别是如果你要在不同的时间释放它们。
您必须考虑的最后一件事是您多久调用一次mallocs。如果频繁,那么多个malloc的成本可能很高。