我可以将变量强制转换为在C中执行时决定的类型吗


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);
}