混合数据类型(int,float,char等)如何存储在数组中?

cha*_*rre 143 c arrays

我想在数组中存储混合数据类型.怎么可能这样做?

Bar*_*mar 240

您可以使数组元素成为有区别的联合,也就是标记联合.

struct {
    enum { is_int, is_float, is_char } type;
    union {
        int ival;
        float fval;
        char cval;
    } val;
} my_array[10];
Run Code Online (Sandbox Code Playgroud)

type成员用于保存union每个数组元素应该使用哪个成员的选择.所以如果你想int在第一个元素中存储一个,你会这样做:

my_array[0].type = is_int;
my_array[0].val.ival = 3;
Run Code Online (Sandbox Code Playgroud)

如果要访问数组的元素,必须先检查类型,然后使用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
}
Run Code Online (Sandbox Code Playgroud)

它由程序员决定,以确保type成员始终对应于存储在中的最后一个值union.

  • +1这是用C编写的许多解释语言的实现 (22认同)
  • 维基百科[Tagged union](http://en.wikipedia.org/wiki/Tagged_union)页面的第一行说:_在计算机科学中,一个标记的联合,也称为变体,变体记录,歧视联合,不相交联合,或总和类型,... _它被重新发明了很多次它有很多名字(有点像字典,哈希,关联数组等). (14认同)
  • @texasbruce也称为"标记联盟".我也用我自己的语言使用这种技术.;) (8认同)

小智 32

使用联合:

union {
    int ival;
    float fval;
    void *pval;
} array[10];
Run Code Online (Sandbox Code Playgroud)

但是,您必须跟踪每个元素的类型.


小智 21

数组元素需要具有相同的大小,这就是为什么它不可能.您可以通过创建变体类型来解决它:

#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;
}
Run Code Online (Sandbox Code Playgroud)

union的元素大小是最大元素的大小,4.


lus*_*oog 8

通过删除内部联合,有一种不同的样式来定义标记联合(通过任何名称)IMO使其更好.这是X Window系统中用于事件之类的样式.

Barmar的答案中的例子给出val了内部联盟的名称.Sp.的答案中的示例使用匿名联合来避免.val.每次访问变体记录时都必须指定.不幸的是,C89或C99中没有"匿名"内部结构和联合.它是一个编译器扩展,因此本质上是不可移植的.

IMO更好的方法是颠倒整个定义.使每个数据类型成为自己的结构,并将标记(类型说明符)放入每个结构中.

typedef struct {
    int tag;
    int val;
} integer;

typedef struct {
    int tag;
    float val;
} real;
Run Code Online (Sandbox Code Playgroud)

然后将它们包装在顶级联合中.

typedef union {
    int tag;
    integer int_;
    real real_;
} record;

enum types { INVALID, INT, REAL };
Run Code Online (Sandbox Code Playgroud)

现在可能看起来我们正在重复自己,我们.但请考虑这个定义很可能被隔离到一个文件中.但是在我们.val.获取数据之前,我们已经消除了指定中间体的噪音.

record i;
i.tag = INT;
i.int_.val = 12;

record r;
r.tag = REAL;
r.real_.val = 57.0;
Run Code Online (Sandbox Code Playgroud)

相反,它走到尽头,不那么令人讨厌.:d

这允许的另一件事是一种继承形式.编辑:这部分不是标准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;
Run Code Online (Sandbox Code Playgroud)

向上铸造和向下铸造.


编辑:需要注意的是,如果您使用C99指定的初始化程序构建其中一个.所有成员初始化者都应该通过同一个联盟成员.

record problem = { .tag = INT, .int_.val = 3 };

problem.tag; // may not be initialized
Run Code Online (Sandbox Code Playgroud)

.tag初始化可以通过优化编译器被忽略,因为.int_初始遵循别名相同的数据区域.即使我们知道布局(!),也应该没问题.不,不是.改为使用"internal"标签(它覆盖外部标签,就像我们想要的那样,但不会混淆编译器).

record not_a_problem = { .int_.tag = INT, .int_.val = 3 };

not_a_problem.tag; // == INT
Run Code Online (Sandbox Code Playgroud)


dza*_*ada 5

你可以void *使用一个单独的数组来做一个数组size_t.但是你丢失了信息类型.
如果需要以某种方式保留信息类型,请保留第三个int数组(其中int是枚举值)然后根据enum值编写转换的函数.