在结构中重新分配数组
realloc of array inside a struct
我正在尝试编写一个函数,它使用 realloc()
来扩展结构实例中指向的数组,但是我似乎无法让它工作。
我的代码的相关部分是:
struct data_t {
int data_size;
uint16_t *data;
};
void extend_data(data_t container, uint16_t value) {
// adds an additional uint16_t to the array of DATA, updates its internal
// variables, and initialises the new uint to VALUE.
int len_data = sizeof(*(container->data)) / sizeof(uint16_t);
printf("LENGTH OF DATA: %d\n", len_data);
container->data = realloc(container->data, sizeof(*(container->data))+sizeof(uint16_t));
container->data_size++;
container->data[container->data_size-1] = value;
len_data = sizeof(*(container->data)) / sizeof(uint16_t);
printf("LENGTH OF DATA: %d\n", len_data);
printf("data_size: %d\n", container->data_size);
return;
}
谁能看出这是什么问题?
void extend_data(data_t container, ...
在您的函数中,container
不是指针,而是通过值传递的结构本身,因此您不能使用 ->
运算符。
当您处理传递结构的本地副本时,重新分配的内存将丢失,并且它将在函数 return 上丢失。
sizeof(*(container.data)) / sizeof(uint16_t)
它将始终是 1
,因为 *(uint16_t *) / sizeof(uint16_t)
始终是一个。
原因:data
成员是指向 uint16_t
的指针。 *data
的类型为 uint16_t
sizeof
是在编译期间而不是运行时计算的,它不会 return malloc
分配的内存量。
编辑
正如 R. Sahu 指出的那样,container
不是此函数中的指针 - 当您说代码 "wasn't working" 时,我假设您的意思是您没有增加数组,但是你在这里写的东西甚至 compile.
您确定您已正确复制此代码吗?如果是这样,"not working" 是否意味着您遇到了编译时错误、运行 时错误,或者只是意外的输出?
如果您已经照原样复制了代码,那么您需要做的第一件事就是将函数原型更改为
void extend_data(data_t *container, uint16_t value) {
并确保将 指针 传递给 data_t
类型,否则更新不会反映在调用代码中。
原创
行中
container->data = realloc(container->data, sizeof(*(container->data))+sizeof(uint16_t));
sizeof(*(container->data))
的计算结果为 sizeof (uint16_t)
。 container->data
是指向 的指针 ,而不是 uint16_t
的数组; sizeof
会给你指针对象的大小,而不是你分配的元素数。你想要做的是类似下面的事情:
/**
* Don't assign the result of a realloc call back to the original
* pointer - if the call fails, realloc will return NULL and you'll
* lose the reference to your original buffer. Assign the result to
* a temporary, then after making sure the temporary is not NULL,
* assign that back to your original pointer.
*/
uint16_t *tmp = realloc(container-data, sizeof *container->data * (container->data_size + 1) );
if ( tmp )
{
/**
* Only add to container->data and update the value of container->data_size
* if the realloc call succeeded.
*/
container->data = tmp;
container->data[container->data_size++] = value;
}
您似乎没有正确使用 sizeof
。在您的结构中,您定义了 uint16_t
pointer,而不是数组。 uint16_t*
数据类型的大小是您系统上指针的大小。如果您希望能够准确地调整它的大小,则需要将分配的内存的大小与指针一起存储。看来您已经有了 data_size
的字段。您的示例可能可以固定为,
// I was unsure of the typedef-ing happening with data_t so I made it more explicit in this example
typedef struct {
int data_size;
uint16_t* data;
} data_t;
void extend_data(data_t* container, uint16_t value) {
// adds an additional uint16_t to the array of DATA, updates its internal
// variables, and initialises the new uint to VALUE.
// CURRENT LENGTH OF DATA
int len_data = container->data_size * sizeof(uint16_t);
printf("LENGTH OF DATA: %d\n", len_data);
uint16_t* tmp = realloc(container->data, (container->data_size + 1) * sizeof(uint16_t));
if (tmp) {
// realloc could fail and return false.
// If this is not handled it could overwrite the pointer in `container` and cause a memory leak
container->data = tmp;
container->data_size++;
container->data[container->data_size-1] = value;
} else {
// Handle allocation failure
}
len_data = container->data_size * sizeof(uint16_t);
printf("LENGTH OF DATA: %d\n", len_data);
printf("data_size: %d\n", container->data_size);
return;
}
您没有正确计算新尺寸。考虑一下:
typedef struct {
size_t size;
int *data;
} int_array;
#define INT_ARRAY_INIT { 0, NULL}
void int_array_resize(int_array *const array,
const size_t newsize)
{
if (!array) {
fprintf(stderr, "int_array_resize(): NULL int_array.\n");
exit(EXIT_FAILURE);
}
if (!newsize) {
free(array->data);
array->data = 0;
array->size = 0;
} else
if (newsize != array->size) {
void *temp;
temp = realloc(array->data, newsize * sizeof array->data[0]);
if (!temp) {
fprintf(stderr, "int_array_resize(): Out of memory.\n");
exit(EXIT_FAILURE);
}
array->data = temp;
array->size = newsize;
}
}
/* int_array my_array = INT_ARRAY_INIT;
is equivalent to
int_array my_array;
int_array_init(&my_array);
*/
void int_array_init(int_array *const array)
{
if (array) {
array->size = 0;
array->data = NULL;
}
}
void int_array_free(int_array *const array)
{
if (array) {
free(array->data);
array->size = 0;
array->data = NULL;
}
}
重点是newsize * sizeof array->data[0]
。这是 array->data[0]
具有的任何类型的 newsize
元素所需的字符数。 malloc()
和 realloc()
都采用字符大小。
如果您使用 int_array my_array = INT_ARRAY_INIT;
初始化该类型的新结构,您只需调用 int_array_resize()
来调整它的大小。 (realloc(NULL, size)
等同于 malloc(size)
;free(NULL)
是安全的,什么都不做。)
int_array_init()
和 int_array_free()
只是用于初始化和释放此类数组的辅助函数。
就个人而言,每当我动态调整数组大小时,我都会同时保留分配的大小 (size
) 和使用的大小 (used
):
typedef struct {
size_t size; /* Number of elements allocated for */
size_t used; /* Number of elements used */
int *data;
} int_array;
#define INT_ARRAY_INIT { 0, 0, NULL }
确保至少可以添加 need
个元素的函数特别有用。为了避免不必要的重新分配,该函数实现了一个策略,该策略计算要分配的新大小,作为内存量 "wasted"(已分配但未使用)和可能较慢的 realloc()
调用数量之间的平衡:
void int_array_need(int_array *const array,
const size_t need)
{
size_t size;
void *data;
if (!array) {
fprintf(stderr, "int_array_need(): NULL int_array.\n");
exit(EXIT_FAILURE);
}
/* Large enough already? */
if (array->size >= array->used + need)
return;
/* Start with the minimum size. */
size = array->used + need;
/* Apply growth/reallocation policy. This is mine. */
if (size < 256)
size = (size | 15) + 1;
else
if (size < 2097152)
size = (3 * size) / 2;
else
size = (size | 1048575) + 1048577 - 8;
/* TODO: Verify (size * sizeof array->data[0]) does not overflow. */
data = realloc(array->data, size * sizeof array->data[0]);
if (!data) {
/* Fallback: Try minimum allocation. */
size = array->used + need;
data = realloc(array->data, size * sizeof array->data[0]);
}
if (!data) {
fprintf(stderr, "int_array_need(): Out of memory.\n");
exit(EXIT_FAILURE);
}
array->data = data;
array->size = size;
}
对于应该使用哪种重新分配策略有很多意见,但这实际上取决于用例。
平衡中有三件事:realloc()
调用的数量,因为它们可能是 "slow";如果需要许多 realloc()
次调用来增长不同的数组,则会产生内存碎片;以及已分配但未使用的内存量。
我上面的政策试图同时做很多事情。对于小分配(最多 256 个元素),它会将大小四舍五入到下一个 16 的倍数。这是我在用于小数组的内存和不是很多 realloc()
调用之间取得良好平衡的尝试。
对于较大的分配,大小增加 50%。这减少了 realloc()
调用的次数,同时将已分配但 unused/unneeded 的内存保持在 50% 以下。
对于非常大的分配,当您有 221 个或更多元素时,大小将四舍五入为 220[=61= 的下一个倍数],少了几个元素。这将已分配但未使用的元素的数量限制为大约 221,即两百万个元素。
(为什么要少一些元素?因为它对任何系统都没有危害,而且在某些系统上它可能会有很大帮助。一些系统,包括 x86-64 (64-bit Intel/AMD) 在某些操作系统和配置,支持大("huge")页面,在某些方面比普通页面更有效。如果它们用于满足分配,我想避免分配超大页面的情况以满足 C 库内部分配元数据所需的几个字节。)
我正在尝试编写一个函数,它使用 realloc()
来扩展结构实例中指向的数组,但是我似乎无法让它工作。
我的代码的相关部分是:
struct data_t {
int data_size;
uint16_t *data;
};
void extend_data(data_t container, uint16_t value) {
// adds an additional uint16_t to the array of DATA, updates its internal
// variables, and initialises the new uint to VALUE.
int len_data = sizeof(*(container->data)) / sizeof(uint16_t);
printf("LENGTH OF DATA: %d\n", len_data);
container->data = realloc(container->data, sizeof(*(container->data))+sizeof(uint16_t));
container->data_size++;
container->data[container->data_size-1] = value;
len_data = sizeof(*(container->data)) / sizeof(uint16_t);
printf("LENGTH OF DATA: %d\n", len_data);
printf("data_size: %d\n", container->data_size);
return;
}
谁能看出这是什么问题?
void extend_data(data_t container, ...
在您的函数中,container
不是指针,而是通过值传递的结构本身,因此您不能使用 ->
运算符。
当您处理传递结构的本地副本时,重新分配的内存将丢失,并且它将在函数 return 上丢失。
sizeof(*(container.data)) / sizeof(uint16_t)
它将始终是 1
,因为 *(uint16_t *) / sizeof(uint16_t)
始终是一个。
原因:data
成员是指向 uint16_t
的指针。 *data
的类型为 uint16_t
sizeof
是在编译期间而不是运行时计算的,它不会 return malloc
分配的内存量。
编辑
正如 R. Sahu 指出的那样,container
不是此函数中的指针 - 当您说代码 "wasn't working" 时,我假设您的意思是您没有增加数组,但是你在这里写的东西甚至 compile.
您确定您已正确复制此代码吗?如果是这样,"not working" 是否意味着您遇到了编译时错误、运行 时错误,或者只是意外的输出?
如果您已经照原样复制了代码,那么您需要做的第一件事就是将函数原型更改为
void extend_data(data_t *container, uint16_t value) {
并确保将 指针 传递给 data_t
类型,否则更新不会反映在调用代码中。
原创
行中
container->data = realloc(container->data, sizeof(*(container->data))+sizeof(uint16_t));
sizeof(*(container->data))
的计算结果为 sizeof (uint16_t)
。 container->data
是指向 的指针 ,而不是 uint16_t
的数组; sizeof
会给你指针对象的大小,而不是你分配的元素数。你想要做的是类似下面的事情:
/**
* Don't assign the result of a realloc call back to the original
* pointer - if the call fails, realloc will return NULL and you'll
* lose the reference to your original buffer. Assign the result to
* a temporary, then after making sure the temporary is not NULL,
* assign that back to your original pointer.
*/
uint16_t *tmp = realloc(container-data, sizeof *container->data * (container->data_size + 1) );
if ( tmp )
{
/**
* Only add to container->data and update the value of container->data_size
* if the realloc call succeeded.
*/
container->data = tmp;
container->data[container->data_size++] = value;
}
您似乎没有正确使用 sizeof
。在您的结构中,您定义了 uint16_t
pointer,而不是数组。 uint16_t*
数据类型的大小是您系统上指针的大小。如果您希望能够准确地调整它的大小,则需要将分配的内存的大小与指针一起存储。看来您已经有了 data_size
的字段。您的示例可能可以固定为,
// I was unsure of the typedef-ing happening with data_t so I made it more explicit in this example
typedef struct {
int data_size;
uint16_t* data;
} data_t;
void extend_data(data_t* container, uint16_t value) {
// adds an additional uint16_t to the array of DATA, updates its internal
// variables, and initialises the new uint to VALUE.
// CURRENT LENGTH OF DATA
int len_data = container->data_size * sizeof(uint16_t);
printf("LENGTH OF DATA: %d\n", len_data);
uint16_t* tmp = realloc(container->data, (container->data_size + 1) * sizeof(uint16_t));
if (tmp) {
// realloc could fail and return false.
// If this is not handled it could overwrite the pointer in `container` and cause a memory leak
container->data = tmp;
container->data_size++;
container->data[container->data_size-1] = value;
} else {
// Handle allocation failure
}
len_data = container->data_size * sizeof(uint16_t);
printf("LENGTH OF DATA: %d\n", len_data);
printf("data_size: %d\n", container->data_size);
return;
}
您没有正确计算新尺寸。考虑一下:
typedef struct {
size_t size;
int *data;
} int_array;
#define INT_ARRAY_INIT { 0, NULL}
void int_array_resize(int_array *const array,
const size_t newsize)
{
if (!array) {
fprintf(stderr, "int_array_resize(): NULL int_array.\n");
exit(EXIT_FAILURE);
}
if (!newsize) {
free(array->data);
array->data = 0;
array->size = 0;
} else
if (newsize != array->size) {
void *temp;
temp = realloc(array->data, newsize * sizeof array->data[0]);
if (!temp) {
fprintf(stderr, "int_array_resize(): Out of memory.\n");
exit(EXIT_FAILURE);
}
array->data = temp;
array->size = newsize;
}
}
/* int_array my_array = INT_ARRAY_INIT;
is equivalent to
int_array my_array;
int_array_init(&my_array);
*/
void int_array_init(int_array *const array)
{
if (array) {
array->size = 0;
array->data = NULL;
}
}
void int_array_free(int_array *const array)
{
if (array) {
free(array->data);
array->size = 0;
array->data = NULL;
}
}
重点是newsize * sizeof array->data[0]
。这是 array->data[0]
具有的任何类型的 newsize
元素所需的字符数。 malloc()
和 realloc()
都采用字符大小。
如果您使用 int_array my_array = INT_ARRAY_INIT;
初始化该类型的新结构,您只需调用 int_array_resize()
来调整它的大小。 (realloc(NULL, size)
等同于 malloc(size)
;free(NULL)
是安全的,什么都不做。)
int_array_init()
和 int_array_free()
只是用于初始化和释放此类数组的辅助函数。
就个人而言,每当我动态调整数组大小时,我都会同时保留分配的大小 (size
) 和使用的大小 (used
):
typedef struct {
size_t size; /* Number of elements allocated for */
size_t used; /* Number of elements used */
int *data;
} int_array;
#define INT_ARRAY_INIT { 0, 0, NULL }
确保至少可以添加 need
个元素的函数特别有用。为了避免不必要的重新分配,该函数实现了一个策略,该策略计算要分配的新大小,作为内存量 "wasted"(已分配但未使用)和可能较慢的 realloc()
调用数量之间的平衡:
void int_array_need(int_array *const array,
const size_t need)
{
size_t size;
void *data;
if (!array) {
fprintf(stderr, "int_array_need(): NULL int_array.\n");
exit(EXIT_FAILURE);
}
/* Large enough already? */
if (array->size >= array->used + need)
return;
/* Start with the minimum size. */
size = array->used + need;
/* Apply growth/reallocation policy. This is mine. */
if (size < 256)
size = (size | 15) + 1;
else
if (size < 2097152)
size = (3 * size) / 2;
else
size = (size | 1048575) + 1048577 - 8;
/* TODO: Verify (size * sizeof array->data[0]) does not overflow. */
data = realloc(array->data, size * sizeof array->data[0]);
if (!data) {
/* Fallback: Try minimum allocation. */
size = array->used + need;
data = realloc(array->data, size * sizeof array->data[0]);
}
if (!data) {
fprintf(stderr, "int_array_need(): Out of memory.\n");
exit(EXIT_FAILURE);
}
array->data = data;
array->size = size;
}
对于应该使用哪种重新分配策略有很多意见,但这实际上取决于用例。
平衡中有三件事:realloc()
调用的数量,因为它们可能是 "slow";如果需要许多 realloc()
次调用来增长不同的数组,则会产生内存碎片;以及已分配但未使用的内存量。
我上面的政策试图同时做很多事情。对于小分配(最多 256 个元素),它会将大小四舍五入到下一个 16 的倍数。这是我在用于小数组的内存和不是很多 realloc()
调用之间取得良好平衡的尝试。
对于较大的分配,大小增加 50%。这减少了 realloc()
调用的次数,同时将已分配但 unused/unneeded 的内存保持在 50% 以下。
对于非常大的分配,当您有 221 个或更多元素时,大小将四舍五入为 220[=61= 的下一个倍数],少了几个元素。这将已分配但未使用的元素的数量限制为大约 221,即两百万个元素。
(为什么要少一些元素?因为它对任何系统都没有危害,而且在某些系统上它可能会有很大帮助。一些系统,包括 x86-64 (64-bit Intel/AMD) 在某些操作系统和配置,支持大("huge")页面,在某些方面比普通页面更有效。如果它们用于满足分配,我想避免分配超大页面的情况以满足 C 库内部分配元数据所需的几个字节。)