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

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

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

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

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

谢谢大家!

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

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

除此之外,您无法使用.访问成员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代码(假设相同的编译器和其他设置),而不必从编译器复制结构内存布局例程。 我不会在我的进程中访问这些结构的字段(只是初始化它们一次),所以方便的语法不是问题。

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

dynstruct.c

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

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

 #include  #include  #include  #include  /* 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; } 

other.c

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

 #include  #include  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"); } 

样本输出

 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 

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

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

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

另一个理论上的可能性是使用诸如libtcc之类的编译器库在运行时编译一些代码。

虽然在理论上非常吸引人(它听起来像是一个自我修改的应用程序 – 你的应用程序只需要为你的结构生成C代码并将其插入模板中,然后请libtcc编译它然后调用模板中定义的一些函数使用该结构),这个解决方案在实践中可能不会很好用。 为什么? 好吧,截至2016年,libtcc(以及整个tcc项目)的开发并不是很积极,而且x86_64等架构存在问题。

对于动态结构,答案是否定的。

如果你知道有什么数据,在C ++中,你可以使用重载的<< in运算符来读取流中的数据。

在C中,您可以将流转换为字符串,假设您知道数据的长度,并使用像sscanf这样的函数,您可以读取数据。

您无法定义源级结构,但您可以通过设置数据结构来存储要通信的数据的每个字段的名称/标记和偏移量,然后在以下位置存储/读取数据。正确的补偿根据。 确保将所有类型对齐到一个边界,该边界是sizeof(type)的倍数以便于移植。 当然,除非你确定客户端和服务器将具有相同的数据表示(字节序和其他考虑因素)并且确实需要直接访问的性能,否则我会编写正确的序列化和反序列化例程…

基于gerty3000的回答,我创建了一个库。 我从最终用户那里抽象了一些东西。 最后工作很难。 如果有任何改进,我愿意接受消化。 这是代码。

type-machine.h //定义类型和函数prototipes

 #ifndef TYPE_MACHINE_H #define TYPE_MACHINE_H #ifdef __cplusplus extern "C" { #endif #define B8 char #define B8U unsigned char #define B16 short #define B16U unsigned short #define B32 int #define B32U unsigned int #define B64 long long int #define B64U unsigned long long int #define BP32 float #define BP64 double #define BIT_ON(var,bit) ((var)=((var) | (bit))) #define BIT_OFF(var,bit) ((var)=((var) & (~bit))) #define BIT_IS_ON(var,bit) (var & bit) #define PAIR(position,value) ((value)=((position) << (1))) typedef struct Bit8Tag BIT; typedef enum { Off, On } STATUS; typedef enum { B8_T, B8U_T, B16_T, B16U_T, B32_T, B64_T, B64U_T, B32U_T, BP32_T, BP64_T } TYPE; typedef struct ClassFieldTag ClassField; typedef struct ClassTag Class; typedef enum { CLASS_SIZE, CLASS_INSERT, CLASS_SHOW } CLASS_MODE; #if (defined(WIN32) || defined(WINDOWS_XP)) #define is_win()(1) #else #define is_win()(0) #define TYPE_CALL #define TYPE_TYPE #endif // WIN32 #include  #include  #include  #define area(a,b) ((a)*(b)) #define radian(x,y)(atan2(y,x)) #define angle(a)( (a * (180 / M_PI)) + 180) #if defined WIN32 #define ARIAL_PATH "C:/Windows/Fonts/arial.ttf\0" #else #define ARIAL_PATH "home/media/TheGreat/\0" #endif struct ClassFieldTag { TYPE type; size_t mem, size, len; B8 name[32]; struct ClassFieldTag * next, *preview; }; extern ClassField * class_set_push(); extern ClassField * class_field_set(ClassField * set, TYPE type, B8 * name, size_t len, size_t mem); extern STATUS class_set_next_back(ClassField ** set, ClassField * next); extern STATUS class_set_next_front(ClassField ** set, ClassField * next); extern STATUS class_insert_back(Class * set, TYPE type, B8 * name, size_t len); extern STATUS class_insert_front(Class * set, TYPE type, B8 * name, size_t len); struct ClassTag { B8 name[32]; void * data; B8 * String; B16 Short; B16U UShort; B32 Int; B32U UInt; B64 Long; B64 ULong; BP32 Float; BP64 Double; ClassField * field; }; Class * class_push(B8 name[32]); extern STATUS class_zero(Class * set, B8 name[32]); extern void class_data_push(Class * set); extern void class_data_pop(Class * set); extern void * class_set_to(Class * set, ClassField * field); extern void class_int_set(Class * set, ClassField * field, B32 value); extern B32 class_int_get(Class * set, ClassField * field); extern void class_double_set(Class * set, ClassField * field, BP64 value); extern BP64 class_double_get(Class * set, ClassField * field); extern void class_string_set(Class * set, ClassField * field, B8 * value); extern B8 * class_string_get(Class * set, ClassField * field); extern void class_mode(Class * set, ClassField * field, CLASS_MODE mode); extern void class_field_pop(Class * set); extern void class_pop(Class * set); extern STATUS class_ex(Class * mine); struct Bit8Tag { unsigned b16 : 16; }; extern void bit_on(BIT * value, int bit); extern void bit_off(BIT * value, int bit); extern STATUS bit_is_on(BIT value, int bit); extern B32U strsub(B8 * data, B8 * key); #ifdef __cplusplus } #endif #endif // TYPE_MACHINE_H 

type-machine.c //声明这些函数

 #include  #include  #include  Class * class_push(B8 name[32]) { Class * set = (Class *) malloc(sizeof (Class)); if(class_zero(set,name)){ return(set); } return(NULL); } void class_data_push(Class * set) { B32 class_size = sizeof (Class), class_field_size = sizeof (ClassField); if (set) { if (class_size < sizeof (set))class_size = sizeof (set); if (class_field_size < sizeof (set->field))class_field_size = sizeof (set->field); } set->data = malloc(class_size + class_field_size + 1); } void class_data_pop(Class * set) { if (set && set->data) { free(set->data); } } void * class_set_to(Class * set, ClassField * field) { if (set && set->data && field) { void * data = (char *) set->data + field->mem; return data; } return (NULL); } void class_int_set(Class * set, ClassField * field, B32 value) { if (set) { assert(field->type == B32_T); B32 * update = class_set_to(set, field); *update = value; } } B32 class_int_get(Class * set, ClassField * field) { if (set) { assert(field->type == B32_T); B32 * data = class_set_to(set, field); return (*data); } return (0); } void class_double_set(Class * set, ClassField * field, BP64 value) { if (set) { assert(field->type == BP64_T); BP64 * update = class_set_to(set, field); *update = value; } } BP64 class_double_get(Class * set, ClassField * field) { if (set) { assert(field->type == BP64_T); BP64 * data = class_set_to(set, field); return (*data); } return (0); } void class_string_set(Class * set, ClassField * field, B8 * value) { if (set && field && field->len > 1 && value) { assert(field->type == B8_T); size_t len = strlen(value); if (len < 2) { len = 2; } if (len > field->len)len = field->len - 1; B8 * buffer = class_set_to(set, field); if (buffer) { memmove(buffer, value, len); buffer[len] = '\0'; } } } B8 * class_string_get(Class * set, ClassField * field) { if (set && field) { assert(field->type == B8_T); B8 * data = class_set_to(set, field); return (data); } return (NULL); } STATUS class_zero(Class * set, B8 * name) { if (set) { set->String = NULL; set->Short = 0; set->UShort = 0; set->Int = 0; set->UInt = 0; set->Long = 0; set->ULong = 0; set->Float = 0; set->Double = 0; set->data = NULL; memset(set->name, 0, sizeof (set->name)); if (name)memmove(set->name, name, strlen(name)); set->field = NULL; return (On); } return (Off); } ClassField * class_set_push() { return (malloc(sizeof (ClassField))); } void class_field_pop(Class * set) { if (set) { ClassField * field = set->field; while (field) { ClassField * next = field->next; if (field) { free(field); field = NULL; } field = next; } } } void class_pop(Class * set) { if (set) { class_data_pop(set); class_field_pop(set); free(set); set = NULL; } } ClassField * class_field_set(ClassField * field, TYPE type, B8 * name, size_t len, size_t mem) { if (field) { size_t lenght = (name) ? strlen(name) : 0; if (lenght > 32) { lenght = 31; } memcpy(field->name, name, lenght); field->name[lenght] = 0; field->type = type; field->mem = mem; field->len = len; class_mode(NULL, field, CLASS_SIZE); field->next = NULL; field->preview = NULL; return (field); } return (NULL); } STATUS class_set_next_back(ClassField ** field, ClassField * next) { if (next == NULL)return (Off); next->next = *field; if (*field != NULL) { (*field)->preview = next; } *field = next; return (On); } STATUS class_set_next_front(ClassField ** field, ClassField * next) { if (next == NULL)return (Off); if (*field != NULL) { ClassField * update = *field, *preview = NULL; while (update->next != NULL) { preview = update; update = update->next; } update->preview = preview; update->next = next; return (On); } *field = next; return (On); } STATUS class_insert_back(Class * set, TYPE type, B8 * name, size_t len) { if (class_set_next_back(&set->field, class_field_set(class_set_push(), type, name, len, 0))) { ClassField * preview = set->field; if (preview->next) { preview->mem = preview->next->mem + preview->next->size; } return (On); } return (Off); } STATUS class_insert_front(Class * set, TYPE type, B8 * name, size_t len) { ClassField * next = class_field_set(class_set_push(), type, name, len, 0); if (class_set_next_front(&set->field, next)) { ClassField * preview = set->field; while (preview) { if (preview->next) { if (preview->next == next) { next->mem = preview->mem + preview->size; } } preview = preview->next; } return (On); } return (Off); } void class_mode(Class * set, ClassField * field, CLASS_MODE mode) { if (field) { switch (field->type) { case B8_T: { switch (mode) { case CLASS_SHOW: { printf("%s: %s\n", field->name, class_string_get(set, field)); } break; case CLASS_SIZE: { field->size = field->len * sizeof (B8); } break; case CLASS_INSERT: { class_string_set(set, field, set->String); } break; } } break; case B8U_T: { switch (mode) { case CLASS_SHOW: { printf("%s: %s\n", field->name, class_string_get(set, field)); } break; case CLASS_SIZE: { field->size = field->len * sizeof (B8U); } break; case CLASS_INSERT: { class_string_set(set, field, set->String); } break; } } break; case B16_T: { switch (mode) { case CLASS_SHOW: { printf("%s: [%i]\n", field->name, class_int_get(set, field)); } break; case CLASS_SIZE: { field->size = sizeof (B16); } break; case CLASS_INSERT: { class_int_set(set, field, set->Int); } break; } } break; case B16U_T: { switch (mode) { case CLASS_SHOW: { printf("%s: [%i]\n", field->name, class_int_get(set, field)); } break; case CLASS_SIZE: { field->size = sizeof (B16U); } break; case CLASS_INSERT: { class_int_set(set, field, set->Int); } break; } } break; case B32_T: { switch (mode) { case CLASS_SHOW: { printf("%s: %i\n", field->name, class_int_get(set, field)); } break; case CLASS_SIZE: { field->size = sizeof (B32); } break; case CLASS_INSERT: { class_int_set(set, field, set->Int); } break; } } break; case B32U_T: { switch (mode) { case CLASS_SHOW: { printf("%s: [%i]\n", field->name, class_int_get(set, field)); } break; case CLASS_SIZE: { field->size = sizeof (B32U); } break; case CLASS_INSERT: { class_int_set(set, field, set->Int); } break; } } break; case B64_T: { switch (mode) { case CLASS_SHOW: { printf("%s: [%i]\n", field->name, class_int_get(set, field)); } break; case CLASS_SIZE: { field->size = sizeof (B64); } break; case CLASS_INSERT: { class_int_set(set, field, set->Int); } break; } } break; case B64U_T: { switch (mode) { case CLASS_SHOW: { printf("%s: [%i]\n", field->name, class_int_get(set, field)); } break; case CLASS_SIZE: { field->size = sizeof (B64U); } break; case CLASS_INSERT: { class_int_set(set, field, set->Int); } break; } } break; case BP32_T: { switch (mode) { case CLASS_SHOW: { printf("%s: [%lf]\n", field->name, class_double_get(set, field)); } break; case CLASS_SIZE: { field->size = sizeof (BP32); } break; case CLASS_INSERT: { class_double_set(set, field, set->Double); } break; } } break; case BP64_T: { switch (mode) { case CLASS_SHOW: { printf("%s: [%lf]\n", field->name, class_double_get(set, field)); } break; case CLASS_SIZE: { field->size = sizeof (BP64); } break; case CLASS_INSERT: { class_double_set(set, field, set->Double); } break; } } break; } } } void bit_on(BIT * value, int bit) { BIT_ON(value->b16, bit); } void bit_off(BIT * value, int bit) { BIT_OFF(value->b16, bit); } STATUS bit_is_on(BIT value, int bit) { if (value.b16 & bit)return (On); return (Off); } B32U strsub(B8 * data, B8 * key) { if (data && key) { B8 *d = data; B32U len = strlen(key), p = 0; if (len > strlen(d))return (0); while (*d != '\0') { if (*(d + len) != '\0') { B32U x = 0; while (x <= len) { if (key[x] == *d) { *d++; p++; } else break; x++; } if (x == len)return (p); } else if (len == 1) { if (*d == key[0])return (p); } p++; *d++; } } return (0); } 

main.c //测试....

 #include "network.h" #include  STATUS class_ex(Class * set) { class_data_push(set); if (set->data) { ClassField * field = set->field; while (field) { if (!strcmp(field->name, "peso")) { set->Double = 65.5; } if (!strcmp(field->name, "idade")) { set->Int = 29; } if (!strcmp(field->name, "nome")) { set->String = "Lisias de Castro Martins"; } if (!strcmp(field->name, "endereco")) { set->String = "Rua Mae D'Agua"; } class_mode(set, field, CLASS_INSERT); class_mode(set, field, CLASS_SHOW); field = field->next; } return (On); } return (Off); } int main(int argc, char** argv) { STATUS client_start = On; if (client_start) { Class * client = class_push("Client");; class_insert_back(client, BP64_T, "peso", 1); class_insert_back(client, B8_T, "endereco", 32); class_insert_back(client, B32_T, "idade", 1); class_insert_back(client, B8_T, "nome", 64); printf("Classe[%s]\n\n", client->name); if (class_ex(client)) { } class_pop(client); getch(); } return (EXIT_SUCCESS); } 

我仍然需要实现短双和其他一些function,但它正在工作。