我最近一直在思考如何在擦除后将类型重新分配给指针(被转换为空指针)。这是我今天早些时候想到的一个方法:
#include<stdio.h>
//represent all possible types to be reassigned
typedef enum types {STRUCT_ONE, STRUCT_TWO, STRUCT_THREE} types;
typedef struct struct_one {
enum types type;
int val1;
double val2;
} struct_one;
typedef struct struct_two {
enum types type;
char val1;
void* val2;
} struct_two;
typedef struct struct_three {
enum types type;
long val1;
int val2[10];
} struct_three;
void reassign(void*);
int main() {
struct_one first;
first.type = STRUCT_ONE; //This member will be used to reassign original type
first.val1 = 64;
first.val2 = 1.2;
struct_two second;
second.type = STRUCT_TWO;
second.val1 = 'b';
second.val2 = &first;
struct_three third;
third.type = STRUCT_THREE;
third.val1 = 1;
third.val2[3] = 3;
reassign(&first);
reassign(&second);
reassign(&third);
return 0;
}
void reassign(void* some_struct) {
types* t = some_struct; //works only if the first member is a type enum
switch(*t) {
case STRUCT_ONE:
struct_one* my_struct1 = some_struct;
printf("%d\t%lf\n", my_struct1 -> val1, my_struct1 -> val2);
break;
case STRUCT_TWO:
struct_two* my_struct2 = some_struct;
printf("%c\t%p\n", my_struct2 -> val1, my_struct2 -> val2);
break;
case STRUCT_THREE:
struct_three* my_struct3 = some_struct;
printf("%ld\t%d\n", my_struct3 -> val1, my_struct3 -> val2[3]);
break;
}
return;
}
总而言之,顶部的枚举代表需要重新分配的所有可能类型。相应的枚举被设置为每个结构的第一个成员。这是因为(至少在 gcc 中)结构没有标头,因此结构指针是指向其第一个成员的指针。即使在被转换为 void 指针之后,该指针仍将指向枚举,因此它可以用于确定结构的原始类型。
输出:
64 1.200000
b 0x7ffc9be166e0
1 3
这似乎按预期工作,但不是很动态。我必须事先确定可以传递到函数中的所有可能类型。不幸的是,据我所知,C 中没有办法将类型引用为离散值(至少在使用 gcc 时不行),因此不可能执行类似以下操作:
if(get_type(operand) == get_type(int))
。否则,我也许可以将结构的第一个成员设置为类型本身,并按每个结构的第一个成员中包含的类型进行强制转换。沿着这些思路:
typedef struct struct_one {
Type t;
} struct_one;
int main() {
struct_one mystruct;
mystruct.t = struct_one;
reassign(&mystruct);
return 0;
}
void reassign(void* some_struct) {
Type* t = some_struct;
typeof *t my_struct = some_struct;
return;
}
我的问题是,即使不是按照书面形式,传真也可能吗?
我承认对 C 相当陌生,所以也许有一些我不知道的运算符或编译器指令可以提供帮助?但在梳理 K&R 时并没有看到任何东西。有什么方法可以修改我的代码,以便它可以将 void 指针强制转换回任何类型,而不仅仅是枚举中定义的类型?
我尝试使用
typeof
运算符,但发现它并不像我想象的那样工作。尝试寻找一种将数据类型存储为变量的方法,但网络上的其他来源表示这是不可能的。尝试按大小识别类型,但意识到这是一个多么糟糕的想法。
考虑这3个
struct
typedef struct
{
int val1;
double val2;
} One;
typedef struct
{
char val1;
void* val2;
} Two;
typedef struct
{
long val1;
int val2[10];
} Three;
如果我们可以有一个标头来表示后面的
struct
的类型,这是网络中的常见情况,其中标头标识消息正文。我将在下面的示例中展示一些处理消息其余部分的常见方法。
我还将使用函数表和选择器函数。当我编写代码时,这可能会变得有点长和无聊。您可以跳转到下面的“完整代码”。
struct Buffer
typedef struct
{
int32_t id;
int (*show)(void*);
union {
One one;
Two two;
Three three;
};
} Buffer;
这使用了
id
字段、指向 show
函数的指针以及代表 3 union
的 struct
。可以随时评估 id
,并且可以根据 show
以及所需字段分配 id
方法。
struct Three
Buffer b3 =
{
.id = S_THREE,
.three.val1 = 17,
.three.val2= { 1,2,3,4,5,6,7,8,9,10 },
.show = show_31
};
作为示例,
b3
设置 id
和 Three
struct
的字段。声明了 show
方法
int show_11(void*);
int show_12(void*);
int show_21(void*);
int show_31(void*);
// takes a void* and call method based on header 'id'
int show(void* addr);
并使用预期的字段和结构。看代码
由于 Expect 方法作为字段加载到
Buffer
中,因此可以通过两种方式调用:
show_31(&b3); // will use show31()
b3.show(&b3);
// using a VFT
int (*F[3])(void*) = {NULL};
F[S_ONE] = show_12;
F[S_TWO] = show_21;
F[S_THREE] = show_31;
这构建了一个可以用作的表
F[ b1.id ](&b1);
F[ b2.id ](&b2);
F[ b3.id ](&b3);
Buffer
作为 void*
int show(void* addr)
{
((Buffer*)addr)->show(addr);
return 0;
}
这使用
addr
作为指向 Buffer
的指针并调用 show
方法,但可以改为使用基于 id
字段的函数指针表。
Version 1 struct type 1
int val1 is 42, double val2 is 42.42
Version 2 struct type 1
(int) val1 is 0000000042, double val2 is 42.4200
Version 1, struct type 2
char val1 is '?', void* val2 points to 00000000
Version 1, struct type 2
char val1 is '?', void* val2 points to 00000000
Version 1, struct type 3
int val1 = 17
int[10] val2 = {1,2,3,4,5,6,7,8,9,10}
Version 1, struct type 3
int val1 = 17
int[10] val2 = {1,2,3,4,5,6,7,8,9,10}
***** now uses a function table to call a method based on the 'id' field *****
Version 2 struct type 1
(int) val1 is 0000000042, double val2 is 42.4200
Version 1, struct type 2
char val1 is '?', void* val2 points to 00000000
Version 1, struct type 3
int val1 = 17
int[10] val2 = {1,2,3,4,5,6,7,8,9,10}
***** now uses a void* pointer and calls the 'show' method' *****
Version 2 struct type 1
(int) val1 is 0000000042, double val2 is 42.4200
Version 1, struct type 2
char val1 is '?', void* val2 points to 00000000
Version 1, struct type 3
int val1 = 17
int[10] val2 = {1,2,3,4,5,6,7,8,9,10}
#pragma pack(show)
#pragma pack(push,4)
#pragma pack(show)
#include<stdio.h>
#include<stdint.h>
typedef enum {S_ONE,S_TWO,S_THREE} types;
typedef struct
{
int val1;
double val2;
} One;
typedef struct
{
char val1;
void* val2;
} Two;
typedef struct
{
long val1;
int val2[10];
} Three;
typedef struct
{
int32_t id;
int (*show)(void*);
union {
One one;
Two two;
Three three;
};
} Buffer;
int show_11(void*);
int show_12(void*);
int show_21(void*);
int show_31(void*);
// takes a void* and call method based on header 'id'
int show(void* addr);
int main()
{
Buffer b1 =
{
.id = S_ONE,
.one.val1 = 42,
.one.val2=42.42,
.show = show_12
};
show_11(&b1);
b1.show(&b1); // will use show12()
Buffer b2 =
{
.id = S_TWO,
.two.val1 = '?',
.two.val2=NULL,
.show=show_21
};
show_21(&b2);
b2.show(&b2); // will use show21()
Buffer b3 =
{
.id = S_THREE,
.three.val1 = 17,
.three.val2= { 1,2,3,4,5,6,7,8,9,10 },
.show = show_31
};
show_31(&b3); // will use show31()
b3.show(&b3);
printf("\n\n***** now uses a function table to call a method based on the 'id' field *****\n\n");
// using a VFT
int (*F[3])(void*) = {NULL};
F[S_ONE] = show_12;
F[S_TWO] = show_21;
F[S_THREE] = show_31;
// call correct 'show' function based on the 'id' field
F[ b1.id ](&b1);
F[ b2.id ](&b2);
F[ b3.id ](&b3);
printf("\n\n***** now uses a void* pointer and calls the 'show' method' *****\n\n");
show((void*) &b1);
void* pThing = &b2;
show(pThing);
show(&b3);
return 0;
}
int show_11(void* p)
{
One* one = &((Buffer*)p)->one;
printf( "\n\
Version 1 struct type 1\n\
int val1 is %d, double val2 is %g\n",
one->val1, one->val2);
return 0;
}
int show_12(void* p)
{
One* one = &((Buffer*)p)->one;
printf( "\n\
Version 2 struct type 1\n\
(int) val1 is %010d, double val2 is %8.4f\n",
one->val1, one->val2);
return 0;
}
int show_21(void* p)
{
Two* t = &((Buffer*)p)->two;
printf( "\n\
Version 1, struct type 2\n\
char val1 is '%c', void* val2 points to %p\n",
t->val1, t->val2);
return 0;
}
int show_31(void* p)
{
Three* t = &((Buffer*)p)->three;
printf( "\n\
Version 1, struct type 3\n\
int val1 = %d\n\
int[10] val2 = {",
t->val1);
for(int i=0;i<9;i+=1) printf("%d,",t->val2[i]);
printf("%d}\n", t->val2[9]);
return 0;
}
int show(void* addr)
{
((Buffer*)addr)->show(addr);
return 0;
}
选择适合您的情况的部分。