int* push_back_int(intVector* target, int push)
{
target->length++;
target->val = (int *)realloc(target->val, target->length * sizeof(int));
target->val[target->length - 1] = push;
return &target->val[target->length - 1];
}
float* push_back_float(floatVector* target, float push)
{
target->length++;
target->val = (float *)realloc(target->val, target->length * sizeof(float));
target->val[target->length - 1] = push;
return &target->val[target->length - 1];
}
有没有什么方法可以让我保存一个变量来替换转换为int*或float*,这样我就可以使用void*为多个变量类型重用相同的代码
否。在C中,该类型仅在编译时可用。
可以使用void *
来回传递数据,但需要保留元素大小。这种方法被称为非类型安全(编译器不会捕捉到错误的"类型",例如,切换下面的iq和fq,当你出错时,它会在运行时爆炸(。请注意调用代码如何处理强制转换。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct queue {
size_t push_length;
void *val;
size_t length;
};
void *push_back(struct queue *target, void *push) {
size_t offset = target->length * target->push_length;
target->length++;
void *tmp = realloc(target->val, target->length * target->push_length);
if(!tmp) {
// error handling
return NULL;
}
target->val = tmp;
return memcpy((char *) target->val + offset, push, target->push_length);
}
int main() {
struct queue fq = { sizeof(float), NULL, 0 };
push_back(&fq, &(float) { 2.718 });
push_back(&fq, &(float) { 3.142 });
for(unsigned i = 0; i < fq.length; i++) {
printf("%u: %fn", i, ((float *) fq.val)[i]);
}
struct queue iq = { sizeof(int), NULL, 0 };
push_back(&iq, &(int) { 1 });
push_back(&iq, &(int) { 2 });
for(unsigned i = 0; i < iq.length; i++) {
printf("%u: %dn", i, ((int *) iq.val)[i]);
}
}
输出:
0: 2.718000
1: 3.142000
0: 1
1: 2
您的平台可能需要对val的每个元素进行特定对齐(即,对于类型Tpush_length = sizeof(T) % alignof(T) ? (sizeof(T) / alignof(T) + 1) * alignof(T) : sizeof(T)
(。
我可以将变量强制转换为执行过程中决定的类型吗(?(
是的,在某些情况下支持可变长度数组。
(double (*)[r])
是对运行时确定的类型的强制转换。演示代码如下:
int r = rand();
double a[r];
double (*b)[r] = &a;
unsigned char *data = malloc(sizeof a);
b = data; // warning: assignment to 'double (*)[r]' from incompatible pointer type 'unsigned char *' [-Wincompatible-pointer-types]
b = (double (*)[r]) data; // OK
(void) b;
在OP的代码中,不需要强制转换和sizeof(type)
两者都使用target->val = realloc(target->val, target->length * sizeof(target->val[0]));
。
push_back_...()
中剩下的唯一区别是函数名称和签名。
是否有任何方法可以保存一个变量来替换到
int*
或float*
的转换,以便使用void*
对多个变量类型重用相同的代码。
任何对象指针都可以保存在void *
中。然而,void *
并不一定保留任何东西来表示它被分配的类型。需要辅助数据。在OP的情况下,如果每个向量的大小是一致的,那么类型的大小就足够了。
typedef struct {
size_t object_size;
size_t length;
void *val;
} gVector;
// Pass in the address of the object to push
void* push_back_g(gVector* target, const void *push) {
void *p = realloc(target->val, target->object_size * (target->length + 1u));
if (p) {
target->val = p;
return memcpy((unsigned char *)val + target->object_size * target->length++,
push, target->object_size);
}
// Handle error with TBD code
return NULL;
}
或者,我们可以传入每次推送调用的大小,并将其存储。
然而,在这两种情况下,代码都会丢失类型检查
使用_Generic
,代码可以用公共代码处理各种预先确定的选择类型。然而OP似乎想要任何类型的。
C没有运行时类型信息(RTTI(,因此您必须通过引入枚举或类似信息来手动破解。然而,我不鼓励在现代C编程中使用enums+void
指针进行类型泛型编程,因为这是类型不安全和笨拙的。
我们可以使用宏基于许多支持的类型生成类型泛型代码,有点像穷人版的C++模板。但这会产生非常难以阅读和维护的代码,因此也不可取。为了记录在案,它可能看起来像这样:
// NOT RECOMMENDED PRACTICE
#define SUPPORTED_TYPES(X)
X(int)
X(float)
#define vector_define(type)
typedef struct
{
type* val;
size_t length;
}type##Vector;
SUPPORTED_TYPES(vector_define)
#define push_back_define(type)
type* push_back_##type (type##Vector* target, type push)
{
target->length++;
target->val = realloc(target->val, target->length * sizeof(type));
target->val[target->length - 1] = push;
return &target->val[target->length - 1];
}
SUPPORTED_TYPES(push_back_define)
正如你所看到的,它开始看起来像是一种不同的语言,对于任何不习惯看";X宏";。
在这种情况下,最好的解决方案可能是使用void*
编写一个版本,但使用_Generic
将其封装在类型安全的宏中。然后,您的向量可以实现为一个单一类型的通用ADT。对于int、float等来说,这不是一个单独的问题。至于如何正确地做到这一点,对于so来说,答案太长了——一个开始的地方是阅读如何在C中进行私有封装?。
如何在不创建适当ADT:的情况下使用_Generic
的简单示例
#include <stdio.h>
void push_back_int (int* target, int push)
{ puts(__func__); }
void push_back_float (float* target, float push)
{ puts(__func__); }
#define push_back(arr,item)
_Generic((arr),
int*: push_back_int,
float*: push_back_float)
((arr), (item) )
int main (void)
{
int* i_arr;
int i_item = 5;
push_back(i_arr, i_item);
float* f_arr;
float f_item = 123.0f;
push_back(f_arr, f_item);
}