我有一个列表,我希望能够放置不同的类型。我有一个函数返回索引处的当前值:
void *list_index(const List * list, int index) {
assert(index < list->size);
return list->data[index];
}
在数组中有多种类型,例如:
typedef struct structA { List *x; char *y; List *z; } structA;
typedef struct structB { List *u; char *w; } structB;
现在为了从数组中获取数据:
structA *A;
structB *B;
for(j=0... ) {
A = list_index(list, j);
B = list_index(list, j);
}
但是现在如何找出返回值的类型?这是否可以使用typeof(我正在使用GCC btw)?
这是否可能,或者我必须做出某种不同的构造?
答案 0 :(得分:2)
您必须使用如here所示的联盟。
答案 1 :(得分:2)
解决这个问题的最佳方法是使用工会。
另一种方法是将列表项memcpy()到适当类型的实际结构(即,不是指针)。这样做的好处是可以使每个List项尽可能小。
第三种方法是将指针类型转换为type punning。只要使用正确的类型或字符取消引用对象,C就允许这样做。
无论哪种方式,您都需要在每个结构中放置一个标识对象类型的代码。编译器无法确定指针指向的内容。即使您可以使用typeof
,也不应该使用char
。这不是C99。
从技术上讲,如果你不使用联合,你将有一个问题是对类型代码进行合法的C99访问,因为你需要对类型进行临时假设,这将违反对象必须的规则被解除引用为他们的实际类型,通过工会或通过char *。但是,由于类型代码必须在每种类型中处于相同的位置(为了有用),这种对标准的常见技术违反实际上不会导致混叠优化错误。
实际上,如果您将类型代码设为char *
,请将其作为结构中的第一件事,并通过gcc -Wall -Wextra
访问它,我想您最终会得到一些代码令人困惑的阅读,但完全符合C99。
以下是一个例子,它传递了#include <stdio.h>
#include <stdlib.h>
struct A {
char typeCode;
int something;
};
struct B {
char typeCode;
double somethingElse;
};
void *getMysteryList();
int main()
{
void **list = getMysteryList();
int i;
for (i = 0; i < 2; ++i)
switch (*(char *) list[i]) {
case 'A':
printf("%d\n", ((struct A *) list[i])->something);
break;
case 'B':
printf("%7.3f\n", ((struct B *) list[i])->somethingElse);
break;
}
return 0;
}
void *getMysteryList()
{
void **v = malloc(sizeof(void *) * 2);
struct A *a = malloc(sizeof(struct A));
struct B *b = malloc(sizeof(struct B));
a->typeCode = 'A';
a->something = 789;
b->typeCode = 'B';
b->somethingElse = 123.456;
v[0] = a;
v[1] = b;
return v;
}
{{1}}
答案 2 :(得分:0)
你有一些选择。请记住,C基本上不是动态类型语言。
你为结构创建一个共同的基础,并在其中放入一个简单的类型指示器。
struct base {
int type_indication:
};
然后
struct structA {
struct base base;
...
};
然后你可以将指针强制转换为(struct base *)
。
答案 3 :(得分:0)
使要放入列表的元素都从公共基类继承。然后,您可以让基类包含标识实际类型的成员。
class base {
public:
typedef enum {
type1,
type2,
type3
} realtype;
virtual realtype whatAmI()=0;
};
class type_one : public base {
public:
virtual base::realtype whatAmI() { return base::type1; };
};
class type_two : public base {
public:
virtual base::realtype whatAmI() { return base::type2; };
};
之后,您将声明您的列表类型:
std::list<base *> mylist;
并且您可以将指向任何派生类型的指针填充到列表中。然后,当你拿出它们时,你可以调用'whatAmI()'来找出将它投射到的内容。
请注意:尝试在C ++中执行此操作意味着您正在以与C ++不匹配的方式执行某些操作。每当你故意躲避像这样的C ++类型系统时,就意味着你放弃了C ++的大部分用处(静态类型检查),并且通常意味着你以后为自己创造了大量的工作,而不仅仅是你调试这个应用程序的第一次迭代,但特别是在维护时。
答案 4 :(得分:0)
C在编译时完全处理类型和键入(没有动态类型),因此一旦你将指针转换为'void *',它就会丢失有关原始类型的任何信息。您可以将其强制转换为原始类型,但您需要通过其他方法知道这是什么。
执行此操作的常用方法是在列表类型中可能存储的所有对象的开头使用某种类型的标记或描述符。例如:
typedef struct structA { int tag; List *x; char *y; List *z; } structA;
typedef struct structB { int tag; List *u; char *w; } structB;
enum tags { structAtype, structBtype };
您需要确保每次创建structA或structB时,都要正确设置标记字段。然后,您可以将从list_index返回的void *转换为int *并使用它来读取标记。
void *elem = list_index(list, index)
switch (*(int *)elem) {
case structAtype:
/* elem is a structA */
:
case structBtype:
/* elem is a structB */