混合数据types(int,float,char等)如何存储在数组中?
我想将混合的数据types存储在一个数组中。 怎么能这样做?
你可以使数组元素成为一个有区别的联合,也就是标记的联合 。
struct { enum { is_int, is_float, is_char } type; union { int ival; float fval; char cval; } val; } my_array[10];
type
成员用于保存union
的哪个成员应该用于每个数组元素的select。 所以如果你想在第一个元素中存储一个int
,你可以这样做:
my_array[0].type = is_int; my_array[0].val.ival = 3;
当你想访问数组的元素时,你必须首先检查types,然后使用union的相应成员。 switch
语句很有用:
switch (my_array[n].type) { case is_int: // Do stuff for integer, using my_array[n].ival break; case is_float: // Do stuff for float, using my_array[n].fval break; case is_char: // Do stuff for char, using my_array[n].cvar break; default: // Report an error, this shouldn't happen }
这是留给程序员来确保type
成员总是对应于union
存储的最后一个值。
使用联合:
union { int ival; float fval; void *pval; } array[10];
不过,你将不得不跟踪每个元素的types。
数组元素需要具有相同的大小,这就是为什么它是不可能的。 你可以通过创build一个变体types来解决它:
#include <stdio.h> #define SIZE 3 typedef enum __VarType { V_INT, V_CHAR, V_FLOAT, } VarType; typedef struct __Var { VarType type; union { int i; char c; float f; }; } Var; void var_init_int(Var *v, int i) { v->type = V_INT; v->i = i; } void var_init_char(Var *v, char c) { v->type = V_CHAR; v->c = c; } void var_init_float(Var *v, float f) { v->type = V_FLOAT; v->f = f; } int main(int argc, char **argv) { Var v[SIZE]; int i; var_init_int(&v[0], 10); var_init_char(&v[1], 'C'); var_init_float(&v[2], 3.14); for( i = 0 ; i < SIZE ; i++ ) { switch( v[i].type ) { case V_INT : printf("INT %d\n", v[i].i); break; case V_CHAR : printf("CHAR %c\n", v[i].c); break; case V_FLOAT: printf("FLOAT %f\n", v[i].f); break; } } return 0; }
联合元素的大小是最大元素的大小,4。
有一个不同的风格定义标签联盟(无论任何名称),IMO通过删除内部联合使它更好用 。 这是X Window系统中用于事件的样式。
Barmar的答案中给出了内部联盟的名称val
。 Sp。答案中的示例使用匿名联合来避免必须指定.val.
每次访问变体logging。 不幸的是,C89或C99中不提供“匿名”的内部结构和联合。 这是一个编译器扩展,因此是固有的不可移植的。
国际海事组织是一个更好的方式来颠倒整个定义。 使每个数据types为自己的结构,并将标记(types说明符)放入每个结构中。
typedef struct { int tag; int val; } integer; typedef struct { int tag; float val; } real;
然后你把这些包装在一个顶层联盟中。
typedef union { int tag; integer int_; real real_; } record; enum types { INVALID, INT, REAL };
现在看来,我们正在重复自己,我们是 。 但是考虑一下,这个定义很可能被孤立到一个文件中。 但是我们已经消除了指定中间值.val.
的噪音.val.
在你得到数据之前。
record i; i.tag = INT; i.int_.val = 12; record r; r.tag = REAL; r.real_.val = 57.0;
相反,它最终在那里不那么令人讨厌。 :d
这允许的另一件事是一种inheritance的forms。 编辑:这部分不是标准的C,但使用GNU扩展。
if (r.tag == INT) { integer x = r; x.val = 36; } else if (r.tag == REAL) { real x = r; x.val = 25.0; } integer g = { INT, 100 }; record rg = g;
上铸和下铸。
编辑:要注意的一个问题是,如果你正在用C99指定的初始值设定项来构build其中的一个。 所有成员初始值设定项应该通过同一个联合成员。
record problem = { .tag = INT, .int_.val = 3 }; problem.tag; // may not be initialized
.tag
初始化程序可以被优化编译器忽略,因为.int_
初始化程序会使用相同的数据区域。 即使我们知道布局(!),它应该没问题 。 不,不是的。 改为使用“内部”标签(它覆盖外部标签,就像我们想要的,但不会混淆编译器)。
record not_a_problem = { .int_.tag = INT, .int_.val = 3 }; not_a_problem.tag; // == INT
你可以做一个void *
数组,使用size_t.
分隔的数组size_t.
但是你失去了信息types。
如果你需要保持信息types的某种方式保持第三个数组的int(其中的int是一个枚举值)然后编码取决于enum
值转换的函数。
联盟是标准的路要走。 但是你也有其他的解决scheme。
一个是标记指针 。 这利用了alignment的内存,地址的低位总是为零。 例如,在32位系统中,指向int的指针必须是4的倍数,低2位必须是0,因此可以使用它来存储值的types。 当然,您需要在解除引用值之前清除这些位。
void* tp; // tagged pointer enum { is_int, is_double, is_char_p, is_char } type; // ... intptr_t addr = (intptr_t)tp & ~0x03; // clear the 2 low bits in the pointer switch ((intptr_t)tp & 0x03) // check the 2 low bits for the type { case is_int: // data is int printf("%d\n", *((int*)addr)); break; case is_double: // data is double printf("%f\n", *((double*)addr)); break; case is_char_p: // data is char* printf("%s\n", (char*)addr); break; case is_char: // data is char printf("%c\n", *((char*)addr)); break; }
如果您可以确保数据是8字节alignment的,那么您将有一个标签位。 在大多数当前的64位系统中,虚拟地址仍然是48位,因此高16位也可以用作标签。
这有一个缺点,如果数据没有存储在任何地方,你将需要更多的内存。 因此,如果数据的types和范围有限,则可以将值直接存储在指针中。 这已经在Chrome的V8引擎中使用,它检查地址的最低有效位,看看是否是一个双精度指针或一个31位有符号值(称为smi – 小整数)。 如果它是一个int,那么Chrome只是执行一个算术右移1位来获取值,否则指针将被解除引用。
在以前的Mozilla Firefox版本中,他们也使用V8等小整数优化,用3个低位来存储types(int,string,object …等)。 但是自从JaegerMonkey开始,他们又采用了另一种方式( Mozilla的新的JavaScript值表示 )。 该值现在总是存储在一个64位双精度variables中。 当double是规范化的时候,它可以直接用于计算。 但是,如果它的高16位都是1,表示一个NaN,则低32位将把地址(在32位计算机中)直接存储到值或值中,其余的16位将被使用存储types。 这种技术被称为NaN-boxing 。 如果您的主要数据types是浮点数据,这是最好的解决scheme,并提供非常好的性能。 在64位机器中也可以使用,因为地址通常只有48位,如上所述。
阅读更多关于上述技巧: https : //wingolog.org/archives/2011/05/18/value-representation-in-javascript-implementations