动态类型重新分配的方法?

问题描述 投票:0回答:1

我最近一直在思考如何在擦除后将类型重新分配给指针(被转换为空指针)。这是我今天早些时候想到的一个方法:

#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
运算符,但发现它并不像我想象的那样工作。尝试寻找一种将数据类型存储为变量的方法,但网络上的其他来源表示这是不可能的。尝试按大小识别类型,但意识到这是一个多么糟糕的想法。

c gcc struct dynamic
1个回答
0
投票

考虑这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);

使用 VFT

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

选择适合您的情况的部分。

© www.soinside.com 2019 - 2024. All rights reserved.