是否可以在C中动态定义结构

Pet*_*erM 23 c struct dynamic

我很确定这最终会成为一个非常明显的问题,这就是为什么我没有找到关于它的更多信息.不过,我认为值得问:)

基本上,使用结构访问数据非常快.如果数据以可以立即作为结构处理的形式从网络中传出,从性能的角度来看,这是非常好的.

但是,是否可以动态定义结构.客户端和服务器应用程序是否可以协商数据流的格式,然后将该定义用作结构?

如果没有,有没有更好的方法呢?

谢谢大家!

Jon*_*ler 20

无法动态定义与编译时结构相同的结构.

创建可以包含与结构等效的信息的动态结构是可能的,但也很困难.对数据的访问不如编译时可用的方便.

除此之外,如果未在编译时定义,则无法somestruct.not_seen_at_compile_time使用.->表示法访问成员.

通过网络通信,还有其他问题需要解决 - 尤其是'endianness'.也就是说,线上的数据可能包括多字节(2,4,8)整数,MSB或LSB将首先发送,但如果一台机器是小端(IA-32,IA-) 64,x86/64)和另一个是big-endian(SPARC,PPC,几乎所有不是来自英特尔),那么数据将需要转换.浮点格式也可能存在问题.有许多标准致力于定义如何通过网络发送数据 - 这不是微不足道的.一些是特定的:IP,TCP,UDP; 其他是一般性的,例如ASN.1.

然而,'不能做动态数据结构'部分限制了事情 - 您必须事先就数据结构是什么以及如何解释它们达成一致.


你是怎样做的?

gerty3000问:

创建可以包含与结构等效的信息的动态结构是可能的,但也很困难.- 你是怎样做的?我想将动态定义的结构传递给其他C代码(假设相同的编译器和其他设置),而不必从编译器复制结构内存布局例程.我不会在我的进程中访问这些结构的字段(只是初始化它们一次),所以方便的语法不是问题.

如果不以某种形状或形式复制内存布局,则无法执行此操作.它可能不一定完全相同,但如果是的话,它可能是最好的.这里有一些示例代码,大致显示了它是如何完成的.

dynstruct.c

这包含基本结构操作材料 - 描述结构和(简单)成员的结构.处理完整数组(而不是字符串)需要更多工作,并且需要为其他类型管理大量的make-work复制.

它还包含一个main()测试代码的程序.它调用other_function(),这表明我在数据结构中定义的结构确实与结构完全匹配.数据确实假设一个64位机器double必须在8字节边界上对齐(因此结构中有一个4字节的孔); 你必须调整一个double可以在4字节边界上的机器的数据.

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* This is the type that will be simulated dynamically */
/*
struct simulated
{
    int     number;
    double  value;
    char    string[32];
};
*/

/* SOF structure.h */
typedef enum Type { INT, DOUBLE, STRING } Type;

typedef struct Descriptor
{
    size_t  offset;
    Type    type;
    size_t  type_size;
    size_t  array_dim;
    char    name[32];
} Descriptor;

typedef struct Structure
{
    size_t      size;
    char        name[32];
    Descriptor *details;
} Structure;

extern void   *allocate_structure(const Structure *structure);
extern void    deallocate_structure(void *structure);
extern void   *pointer_to_element(void *p, const Descriptor *d);
extern int     get_int_element(void *p, const Descriptor *d);
extern void    set_int_element(void *p, const Descriptor *d, int newval);
extern double  get_double_element(void *p, const Descriptor *d);
extern void    set_double_element(void *p, const Descriptor *d, double newval);
extern char   *get_string_element(void *p, const Descriptor *d);
extern void    set_string_element(void *p, const Descriptor *d, char *newval);
/* EOF structure.h */

static Descriptor details[] =
{
    {   0,  INT,    sizeof(int),     1, "number"    },
    {   8,  DOUBLE, sizeof(double),  1, "value"     },
    {  16,  STRING, sizeof(char),   32, "string"    },
};

static Structure simulated = { 48, "simulated", details };

void *allocate_structure(const Structure *structure)
{
    void *p = calloc(1, structure->size);
    return p;
}

void deallocate_structure(void *structure)
{
    free(structure);
}

void *pointer_to_element(void *p, const Descriptor *d)
{
    void *data = (char *)p + d->offset;
    return data;
}

int get_int_element(void *p, const Descriptor *d)
{
    assert(d->type == INT);
    int *v = pointer_to_element(p, d);
    return *v;
}

void set_int_element(void *p, const Descriptor *d, int newval)
{
    assert(d->type == INT);
    int *v = pointer_to_element(p, d);
    *v = newval;
}

double get_double_element(void *p, const Descriptor *d)
{
    assert(d->type == DOUBLE);
    double *v = pointer_to_element(p, d);
    return *v;
}

void set_double_element(void *p, const Descriptor *d, double newval)
{
    assert(d->type == DOUBLE);
    double *v = pointer_to_element(p, d);
    *v = newval;
}

char *get_string_element(void *p, const Descriptor *d)
{
    assert(d->type == STRING);
    char *v = pointer_to_element(p, d);
    return v;
}

void set_string_element(void *p, const Descriptor *d, char *newval)
{
    assert(d->type == STRING);
    assert(d->array_dim > 1);
    size_t len = strlen(newval);
    if (len > d->array_dim)
        len = d->array_dim - 1;
    char *v = pointer_to_element(p, d);
    memmove(v, newval, len);
    v[len] = '\0';
}

extern void other_function(void *p);

int main(void)
{
    void *sp = allocate_structure(&simulated);

    if (sp != 0)
    {
        set_int_element(sp, &simulated.details[0], 37);
        set_double_element(sp, &simulated.details[1], 3.14159);
        set_string_element(sp, &simulated.details[2], "Absolute nonsense");
        printf("Main (before):\n");
        printf("Integer: %d\n", get_int_element(sp, &simulated.details[0]));
        printf("Double:  %f\n", get_double_element(sp, &simulated.details[1]));
        printf("String:  %s\n", get_string_element(sp, &simulated.details[2]));
        other_function(sp);
        printf("Main (after):\n");
        printf("Integer: %d\n", get_int_element(sp, &simulated.details[0]));
        printf("Double:  %f\n", get_double_element(sp, &simulated.details[1]));
        printf("String:  %s\n", get_string_element(sp, &simulated.details[2]));

        deallocate_structure(sp);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

other.c

这段代码对结构描述材料一无所知dynstruct.c; 它知道struct simulated模拟代码模拟的.它打印传递的数据并对其进行修改.

#include <stdio.h>
#include <string.h>

extern void other_function(void *p);

struct simulated
{
    int     number;
    double  value;
    char    string[32];
};

void other_function(void *p)
{
    struct simulated *s = (struct simulated *)p;

    printf("Other function:\n");
    printf("Integer: %d\n", s->number);
    printf("Double:  %f\n", s->value);
    printf("String:  %s\n", s->string);

    s->number *= 2;
    s->value  /= 2;
    strcpy(s->string, "Codswallop");
}
Run Code Online (Sandbox Code Playgroud)

样本输出

Main (before):
Integer: 37
Double:  3.141590
String:  Absolute nonsense
Other function:
Integer: 37
Double:  3.141590
String:  Absolute nonsense
Main (after):
Integer: 74
Double:  1.570795
String:  Codswallop
Run Code Online (Sandbox Code Playgroud)

显然,这段代码还没有准备就绪.这足以证明可以做些什么.您必须处理的一个问题是正确初始化StructureDescriptor数据.你不能在这种代码中加入过多的断言.例如,我要真有assert(d->size == sizeof(double);get_double_element().包括assert(d->offset % sizeof(double) == 0);确保double元素正确对齐也是明智的.或者您可能具有执行validate_structure(const Structure *sp);所有这些验证检查的功能.您需要一个函数void dump_structure(FILE *fp, const char *tag, const Structure *sp);将定义的结构转储到标记前面的给定文件,以帮助调试.等等.

这段代码是纯C; 它不能被C++编译器编译为C++.没有足够的强制转换来满足C++编译器.


小智 9

不,它不在C中,所有数据类型必须在编译时才知道.这就是它"真正快速"的原因.