如何正确地将 void 类型转换为数组
How to properly cast to a void type as an array
我正在用 C 解决一个问题,我想创建一个动态增长的数组,如果可能的话,对不同的数据类型使用相同的函数。目前我有一个名为 Array
的结构,它使用一个名为 *array
的空数据类型,它是一个指向数组的指针。它还包含 len
存储数组的活动长度,size
存储分配内存的长度,以及 elem
存储用于动态增长的数据类型的长度数组。
另外,我正在使用三个函数。函数 initiate_array
为结构中的 array
变量分配内存并实例化除了一个结构元素之外的所有元素。函数 init_array
充当 initiate_array
的包装器,并在结构中实例化变量 elem
。最后,函数 append_array
将 data/indices 添加到数组并在必要时重新分配内存。
此时Array
结构,函数initiate_array
和init_array
与数据类型无关;但是,append_array
是针对 int
变量进行硬编码的。我试图通过将输入 int item
变为 void item
来使 append_array
在某种程度上独立于数据类型,但随后我在每个位置都收到一个编译时错误,代码为 ((int *)array->array)[array->len - 1] = item
告诉我我不能转换为 void。
我的代码如下,有人建议我如何实现 append_array
函数以独立于 item
的数据类型吗?
注意:我还有一个在执行结束时释放内存的函数,但我从这个问题中省略了它,因为它不相关。
array.h
#ifndef ARRAY_H
#define ARRAY_H
#include <stdlib.h>
#include <stdio.h>
typedef struct
{
void *array;
size_t len;
size_t size;
int elem;
} Array;
void initiate_array(Array *array, size_t num_indices);
Array init_array(int size, size_t num_indices);
void append_array(Array *array, int item);
#endif /* ARRAY_H */
array.c
#include "array.h"
void initiate_array(Array *array, size_t num_indices) {
void *pointer;
pointer = malloc(num_indices * array->elem);
if (pointer == NULL) {
printf("Unable to allocate memory, exiting.\n");
free(pointer);
exit(0);
}
else {
array->array = pointer;
array->len = 0;
array->size = num_indices;
}
}
Array init_array(int size, size_t num_indices) {
Array array;
array.elem = size;
initiate_array(&array, num_indices);
return array;
}
void append_array(Array *array, int item) {
array->len++;
if (array->len == array->size){
array->size *= 2;
void *pointer;
pointer = realloc(array->array, array->size * array->elem);
if (pointer == NULL) {
printf("Unable to reallocate memory, exiting.\n");
free(pointer);
exit(0);
}
else {
array->array = pointer;
((int *)array->array)[array->len - 1] = item;
}
}
else
((int *)array->array)[array->len - 1] = item;
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include "array.h"
int main(int argc, char** argv)
{
int i, j;
size_t indices = 20;
Array pointers = int_array(sizeof(int), indices);
for (i = 0; i < 50; i++)
{
append_int_array(&pointers, i);
}
for (i = 0; i < pointers.len; i++)
{
printf("Value: %d Size:%zu \n",((int *) pointers.array)[i], pointers.len);
}
return (EXIT_SUCCESS);
}
类型void
是一个不完整的类型,不能完成,所以你不能给它赋值或从它赋值,也不能把它用作数组参数类型。
你可以做的是更改 append_array
以将 void *
作为参数指向要添加的数据。然后将数据指针转换为 char *
,这样您就可以进行单字节指针运算以获得正确的偏移量,然后使用 memcpy
复制数据。
void append_array(Array *array, void *item) {
array->len++;
if (array->len == array->size){
array->size *= 2;
void *pointer;
pointer = realloc(array->array, array->size * array->elem);
if (pointer == NULL) {
printf("Unable to reallocate memory, exiting.\n");
free(array->array);
exit(0);
}
else {
array->array = pointer;
}
}
char *p = (char *)array->array + (array->len - 1) * array->elem;
memcpy(p, item, array->elem);
}
您将无法通过传递整数文字来调用此函数来添加,但您可以使用复合文字的地址。
append_array(array, &(int){ 3 });
应该是这样的,或者你可以用typeof
改进一下。
或者,使用 void **array;
#include <assert.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct {
void *array;
size_t size;
size_t capacity;
int elem_size;
} Array;
void initiate_array(Array *array, size_t num_indices);
Array init_array(int size, size_t num_indices);
void append_array(Array *array, void *item);
void initiate_array(Array *array, size_t num_indices) {
void *pointer;
pointer = malloc(num_indices * array->elem_size);
if (pointer == NULL) {
printf("Unable to allocate memory, exiting.\n");
// free(pointer);
exit(0);
} else {
array->array = pointer;
array->size = 0;
array->capacity = num_indices;
}
}
Array init_array(int elem_size, size_t num_indices) {
Array array;
array.elem_size = elem_size;
initiate_array(&array, num_indices);
return array;
}
void append_array(Array *array, void *item) {
if (array->size == array->capacity) {
// extend the array
}
memcpy(array->array + array->size * array->elem_size, item,
array->elem_size);
array->size++;
}
int main(void) {
Array arr = init_array(sizeof(int), 10);
int item = 1;
append_array(&arr, &item);
item = 2;
append_array(&arr, &item);
item = 3;
append_array(&arr, &item);
return 0;
}
我会先看看 append_array
。 void
不是一个完整的类型,也就是说你不能按值传入 void
个对象。不过,您可以通过引用传递它们,并且 void *
也可以引用任何其他类型:
void append_array(Array *array, void *item) {
现在,您假设要传入单个元素。但为什么要停在那里呢?你可以让你的函数签名看起来像这样:
void append_array(Array *array, void *items, size_t count) {
这里的附加警告是 array->size * 2
可能不足以保存附加数据。您可以改用 (array->len + count) * 2
。
假设 array->size
足够大,您可以直接使用 memcpy
复制元素并转换为 char *
,标准保证具有 size-1 元素:
memcpy((char *)array->array + array->len * array->elem, items, count * array->elem);
请注意,我在这里使用 array->len
作为索引。那是因为我的下一个建议是仅在制作副本后增加 array->len
。这将使您的尺寸检查更简单,并且不会像现在这样重新分配一个元素。请记住 array->len
不仅是数组的大小,它也是您要附加到的基于零的索引。
if (array->len + count > array->size) {
对于单个元素,条件为
if (array->len >= array->size) {
最后,我强烈建议您 return 一个整数错误代码而不是退出。此功能的用户应该期望能够在内存错误的情况下至少进行清理,或者可能释放缓存元素,而不是单方面崩溃。
这是最终函数的样子:
int append_array(Array *array, void *items, size_t count)
{
if (array->len + count > array->size) {
size_t size = (array->len + count) * 2;
void *pointer = realloc(array->array, size * array->elem);
if (pointer == NULL) {
return 0;
}
array->array = pointer;
array->size = size;
}
memcpy((char *)array->array + array->len * array->elem, items, count * array->elem);
array->len += count;
return 1;
}
以这种方式编写函数有一个小缺点:由于右值没有地址,您不能调用
append_array(&array, &3, 1);
您可以通过两种方式解决此问题。
创建一个临时变量或缓冲区来保存值:
int tmp = 3;
append_array(&array, &tmp, 1);
制作一个 type-specific 可以接受完整类型元素的包装器。这是可行的,因为 C 是纯粹的 pass-by-value(即复制),所以你可以做
int append_int(Array *array, int value)
{
return append_array(array, &value, 1);
}
在这种情况下,您实际上是在使用新的堆栈帧来保存第一个示例中 tmp
的值。
我正在用 C 解决一个问题,我想创建一个动态增长的数组,如果可能的话,对不同的数据类型使用相同的函数。目前我有一个名为 Array
的结构,它使用一个名为 *array
的空数据类型,它是一个指向数组的指针。它还包含 len
存储数组的活动长度,size
存储分配内存的长度,以及 elem
存储用于动态增长的数据类型的长度数组。
另外,我正在使用三个函数。函数 initiate_array
为结构中的 array
变量分配内存并实例化除了一个结构元素之外的所有元素。函数 init_array
充当 initiate_array
的包装器,并在结构中实例化变量 elem
。最后,函数 append_array
将 data/indices 添加到数组并在必要时重新分配内存。
此时Array
结构,函数initiate_array
和init_array
与数据类型无关;但是,append_array
是针对 int
变量进行硬编码的。我试图通过将输入 int item
变为 void item
来使 append_array
在某种程度上独立于数据类型,但随后我在每个位置都收到一个编译时错误,代码为 ((int *)array->array)[array->len - 1] = item
告诉我我不能转换为 void。
我的代码如下,有人建议我如何实现 append_array
函数以独立于 item
的数据类型吗?
注意:我还有一个在执行结束时释放内存的函数,但我从这个问题中省略了它,因为它不相关。
array.h
#ifndef ARRAY_H
#define ARRAY_H
#include <stdlib.h>
#include <stdio.h>
typedef struct
{
void *array;
size_t len;
size_t size;
int elem;
} Array;
void initiate_array(Array *array, size_t num_indices);
Array init_array(int size, size_t num_indices);
void append_array(Array *array, int item);
#endif /* ARRAY_H */
array.c
#include "array.h"
void initiate_array(Array *array, size_t num_indices) {
void *pointer;
pointer = malloc(num_indices * array->elem);
if (pointer == NULL) {
printf("Unable to allocate memory, exiting.\n");
free(pointer);
exit(0);
}
else {
array->array = pointer;
array->len = 0;
array->size = num_indices;
}
}
Array init_array(int size, size_t num_indices) {
Array array;
array.elem = size;
initiate_array(&array, num_indices);
return array;
}
void append_array(Array *array, int item) {
array->len++;
if (array->len == array->size){
array->size *= 2;
void *pointer;
pointer = realloc(array->array, array->size * array->elem);
if (pointer == NULL) {
printf("Unable to reallocate memory, exiting.\n");
free(pointer);
exit(0);
}
else {
array->array = pointer;
((int *)array->array)[array->len - 1] = item;
}
}
else
((int *)array->array)[array->len - 1] = item;
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include "array.h"
int main(int argc, char** argv)
{
int i, j;
size_t indices = 20;
Array pointers = int_array(sizeof(int), indices);
for (i = 0; i < 50; i++)
{
append_int_array(&pointers, i);
}
for (i = 0; i < pointers.len; i++)
{
printf("Value: %d Size:%zu \n",((int *) pointers.array)[i], pointers.len);
}
return (EXIT_SUCCESS);
}
类型void
是一个不完整的类型,不能完成,所以你不能给它赋值或从它赋值,也不能把它用作数组参数类型。
你可以做的是更改 append_array
以将 void *
作为参数指向要添加的数据。然后将数据指针转换为 char *
,这样您就可以进行单字节指针运算以获得正确的偏移量,然后使用 memcpy
复制数据。
void append_array(Array *array, void *item) {
array->len++;
if (array->len == array->size){
array->size *= 2;
void *pointer;
pointer = realloc(array->array, array->size * array->elem);
if (pointer == NULL) {
printf("Unable to reallocate memory, exiting.\n");
free(array->array);
exit(0);
}
else {
array->array = pointer;
}
}
char *p = (char *)array->array + (array->len - 1) * array->elem;
memcpy(p, item, array->elem);
}
您将无法通过传递整数文字来调用此函数来添加,但您可以使用复合文字的地址。
append_array(array, &(int){ 3 });
应该是这样的,或者你可以用typeof
改进一下。
或者,使用 void **array;
#include <assert.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct {
void *array;
size_t size;
size_t capacity;
int elem_size;
} Array;
void initiate_array(Array *array, size_t num_indices);
Array init_array(int size, size_t num_indices);
void append_array(Array *array, void *item);
void initiate_array(Array *array, size_t num_indices) {
void *pointer;
pointer = malloc(num_indices * array->elem_size);
if (pointer == NULL) {
printf("Unable to allocate memory, exiting.\n");
// free(pointer);
exit(0);
} else {
array->array = pointer;
array->size = 0;
array->capacity = num_indices;
}
}
Array init_array(int elem_size, size_t num_indices) {
Array array;
array.elem_size = elem_size;
initiate_array(&array, num_indices);
return array;
}
void append_array(Array *array, void *item) {
if (array->size == array->capacity) {
// extend the array
}
memcpy(array->array + array->size * array->elem_size, item,
array->elem_size);
array->size++;
}
int main(void) {
Array arr = init_array(sizeof(int), 10);
int item = 1;
append_array(&arr, &item);
item = 2;
append_array(&arr, &item);
item = 3;
append_array(&arr, &item);
return 0;
}
我会先看看 append_array
。 void
不是一个完整的类型,也就是说你不能按值传入 void
个对象。不过,您可以通过引用传递它们,并且 void *
也可以引用任何其他类型:
void append_array(Array *array, void *item) {
现在,您假设要传入单个元素。但为什么要停在那里呢?你可以让你的函数签名看起来像这样:
void append_array(Array *array, void *items, size_t count) {
这里的附加警告是 array->size * 2
可能不足以保存附加数据。您可以改用 (array->len + count) * 2
。
假设 array->size
足够大,您可以直接使用 memcpy
复制元素并转换为 char *
,标准保证具有 size-1 元素:
memcpy((char *)array->array + array->len * array->elem, items, count * array->elem);
请注意,我在这里使用 array->len
作为索引。那是因为我的下一个建议是仅在制作副本后增加 array->len
。这将使您的尺寸检查更简单,并且不会像现在这样重新分配一个元素。请记住 array->len
不仅是数组的大小,它也是您要附加到的基于零的索引。
if (array->len + count > array->size) {
对于单个元素,条件为
if (array->len >= array->size) {
最后,我强烈建议您 return 一个整数错误代码而不是退出。此功能的用户应该期望能够在内存错误的情况下至少进行清理,或者可能释放缓存元素,而不是单方面崩溃。
这是最终函数的样子:
int append_array(Array *array, void *items, size_t count)
{
if (array->len + count > array->size) {
size_t size = (array->len + count) * 2;
void *pointer = realloc(array->array, size * array->elem);
if (pointer == NULL) {
return 0;
}
array->array = pointer;
array->size = size;
}
memcpy((char *)array->array + array->len * array->elem, items, count * array->elem);
array->len += count;
return 1;
}
以这种方式编写函数有一个小缺点:由于右值没有地址,您不能调用
append_array(&array, &3, 1);
您可以通过两种方式解决此问题。
创建一个临时变量或缓冲区来保存值:
int tmp = 3; append_array(&array, &tmp, 1);
制作一个 type-specific 可以接受完整类型元素的包装器。这是可行的,因为 C 是纯粹的 pass-by-value(即复制),所以你可以做
int append_int(Array *array, int value) { return append_array(array, &value, 1); }
在这种情况下,您实际上是在使用新的堆栈帧来保存第一个示例中
tmp
的值。