我想动态分配一个C结构体:
typedef struct {
short *offset;
char *values;
} swc;
'offset'和'values'都应该是数组,但它们的大小在运行时是未知的。
我该如何为我的结构体及其数组动态分配内存?
我想动态分配一个C结构体:
typedef struct {
short *offset;
char *values;
} swc;
'offset'和'values'都应该是数组,但它们的大小在运行时是未知的。
我该如何为我的结构体及其数组动态分配内存?
swc *a = (swc*)malloc(sizeof(swc));
a->offset = (short*)malloc(sizeof(short)*n);
a->values = (char*)malloc(sizeof(char)*n);
n是每个数组中的项目数,a是新分配的数据结构的地址。在释放a之前,不要忘记free()偏移量和值。
malloc()
返回值的强制转换是一个有争议的做法,所以不要表现得你支持的一方是“正确”的。这个问题已经被讨论了很多次,而且这些评论不是复制辩论的地方。如果你需要C++兼容性,那么你别无选择,但如果不需要,我建议你不要这样做,因为如果a->offset
的类型更改为long
,你将不得不更改malloc()
(和任何你可能调用的realloc()
)的强制转换,否则会导致严重的错误,这会比必要的维护更加麻烦。(接下来的内容见下一条评论) - Chris Lutzsizeof(*a)
和sizeof(*a->offset)
而不是sizeof(swc)
和sizeof(short)
。如果a
的类型发生更改(或更可能的是,a->offset
的类型从short变为long),使用变量而不是类型将使您仅更改声明变量的行,而不是更改所有调用malloc()
和realloc()
的行。如果它们都能从变量(或结构体成员)中推断出正确的类型,那么如果我们必须将该类型更改为另一种类型,我们就会少些麻烦。每个人都受益。 - Chris Lutz在C语言中:
swc *s = malloc(sizeof *s); // assuming you're creating a single instance of swc
if (s)
{
s->offset = malloc(sizeof *(s->offset) * number_of_offset_elements);
s->values = malloc(sizeof *(s->values) * number_of_value_elements);
}
在C++中:
try
{
swc *s = new swc;
s->offset = new short[number_of_offset_elements];
s->values = new char[number_of_value_elements];
}
catch(...)
{
...
}
请注意,在C++中,与动态分配缓冲区相比,您可能更好地使用向量(vectors):
struct swc
{
std::vector<short> offset;
std::vector<char> values;
};
swc *a = new swc;
问题:values 应该是一个单个字符的数组还是字符串的数组?这会有所不同。
编辑
我越想,就越不满意这个 C++ 的答案;在 C++ 中做这种事情的正确方式(假设你需要动态分配缓冲区而不是向量,但你可能并不需要)是将偏移和值的内存分配作为结构类型中的构造函数的一部分,并在结构实例被销毁时由析构函数来释放这些元素(通过delete
或超出范围)。
struct swc
{
swc(size_t numOffset = SOME_DEFAULT_VALUE,
size_t numValues = SOME_OTHER_DEFAULT_VALUE)
{
m_offset = new short[numOffset];
m_values = new char[numValues];
}
~swc()
{
delete[] m_offset;
delete[] m_values;
}
short *m_offset;
char *m_values;
};
void foo(void)
{
swc *a = new swc(10,20); // m_offset and m_values allocated as
// part of the constructor
swc b; // uses default sizes for m_offset and m_values
...
a->m_offset[0] = 1;
a->m_values[0] = 'a';
b.m_offset[0] = 2;
b.m_values[0] = 'b';
...
delete a; // handles freeing m_offset and m_values
// b's members are deallocated when it goes out of scope
}
malloc()
的返回值,以及3) 使用sizeof var
而不是sizeof(type)
。虽然说,我可能会考虑使用std::string
来替代std::vector<char>
。 - Chris Lutzswc *pSwc = malloc(sizeof(swc));
pSwc->offset = malloc(sizeof(short)*offsetArrayLength);
pSwc->values = malloc(valuesArrayLength);
在C++中,你不应该做任何像那样的事情。
typedef struct
{
short *offset;
char *values;
} swc;
/// Pre-Condition: None
/// Post-Condition: On failure will return NULL.
/// On Success a valid pointer is returned where
/// offset[0-n) and values[0-n) are legally de-refrancable.
/// Ownership of this memory is returned to the caller who
/// is responsible for destroying it via destroy_swc()
swc *create_swc(unsigned int size)
{
swc *data = (swc*) malloc(sizeof(swc));
if (data)
{
data->offset = (short*)malloc(sizeof(short)*n);
data->values = (char*) malloc(sizeof(char) *n);
}
if ((data != NULL) && (size != 0) && ((data->offset == NULL) || (data->values == NULL)))
{
// Partially created object is dangerous and of no use.
destroy_swc(data);
data = NULL;
}
return data;
}
void destroy_swc(swc* data)
{
free(data->offset);
free(data->values);
free(data);
}
在C++中
struct swc
{
std::vector<short> offset;
std::vector<char> values;
swc(unsigned int size)
:offset(size)
,values(size)
{}
};
malloc()
的返回值进行强制类型转换。如果offset
的类型从short
变为int
(或long
或long long
),那么强制类型转换会导致需要修改一行代码。我宁愿使用data->offset = malloc(sizeof(*data->offset) * n)
,这样即使offset
的类型发生变化也能正常工作。 - Chris Lutzsize
为零,则 malloc
可能有效地返回 NULL
,因此在决定是否有部分创建的对象之前,您应该先检查它,或者将 "size 为正" 添加为前提条件。 - Rob Kennedyswc* makeStruct(int offsetCount, int valuesCount) {
swc *ans = new swc();
ans->offset = new short[offsetCount];
ans->values = new char[valuesCount];
return ans;
}
myNewStruct = makeStruct(4, 20);
语法可能有些问题,但通常这就是你需要的。如果你正在使用C++,那么你可能需要一个带有构造函数的类来接受这两个参数,而不是使用makeStruct,但做的事情非常相似。
在这里,有很多正确的答案,但需要补充一点:你可以使用malloc
来分配一个过大的结构体,以容纳最后一个成员的可变大小数组。
struct foo {
short* offset;
char values[0]
};
以及之后
struct *foo foo1 = malloc(sizeof(struct foo)+30); // takes advantage of sizeof(char)==1
values
数组有30个对象的空间。你仍需要执行相应操作。foo1->offsets = malloc(30*sizeof(short));
swc* a = malloc(sizeof(*a));
a->offset = calloc(n, sizeof(*(a->offset)));
a->values = calloc(n, sizeof(*(a->values)));
在 C 语言中,不应该使用 void* 类型。但在 C++ 中,你必须使用它!
The calloc function initializes allocated memory to zero.
大部分的答案都是正确的。我想补充一些你没有明确提出但可能也很重要的事情。
C / C++ 数组不会在内存中存储它们自己的大小。因此,除非你希望 offset
和 values
具有编译时定义的值(在这种情况下,最好使用固定大小的数组),否则你可能希望将两个数组的大小存储在 struct
中。
typedef struct tagswc {
short *offset;
char *values;
// EDIT: Changed int to size_t, thanks Chris Lutz!
size_t offset_count;
size_t values_count; // You don't need this one if values is a C string.
} swc;
免责声明:我可能是错的。例如,如果所有swc
实例的所有offset
都具有相同的大小,则最好将offset_count
存储为全局成员,而不是struct
的成员。对于values
和values_count
也是如此。此外,如果values
是C字符串,则不需要存储其大小,但要注意类似于Schlemiel the painter的问题。
int
来存储大小。你不想让任何东西的大小为 -12,对吧?使用专门用于存储大小的类型 size_t
。 - Chris Lutz既然还没有人提到它,有时候一次性分配这块内存是很好的选择,这样你只需要在一个东西上调用free():
swc* AllocSWC(int items)
{
int size = sizeof(swc); // for the struct itself
size += (items * sizeof(short)); // for the array of shorts
size += (items * sizeof(char)); // for the array of chars
swc* p = (swc*)malloc(size);
memset(p, 0, size);
p->offset = (short*)((char*)swc + sizeof(swc)); // array of shorts begins immediately after the struct
p->values = (char*)((char*)swc + sizeof(swc) + items * sizeof(short)); // array of chars begins immediately after the array of shorts
return p;
}
当然,这种方法阅读和维护起来会更加困难(特别是在首次分配数组后动态调整大小的情况下)。这只是我在许多地方看到的另一种替代方法。
size_t
替代 int
。