C中的堆栈通常依赖于用于声明它们的数据类型。例如,
int arr[5]; //creates an integer array of size 5 for stack use
char arr[5]; //creates a character array of size 5 for stack use
既限于分别保存整数和字符数据类型,又假定程序员知道在运行时期间生成了什么数据。如果我想要一个可以容纳任何数据类型的堆栈怎么办?
我最初想过将它作为一个联盟来实现,但这种方法不仅困难而且有缺陷。还有其他建议吗?
答案 0 :(得分:4)
我会使用这样的结构:
struct THolder
{
int dataType; // this is a value representing the type
void *val; // this is the value
};
然后使用THolder
数组来存储您的值。
答案 1 :(得分:2)
这实际上只是Pablo Santa Cruz回答的一个变种,但我觉得它看起来更整洁:
typedef enum { integer, real, other } type_t;
typedef struct {
type_t type;
union {
int normal_int; /* valid when type == integer */
double large_float; /* valid when type == real */
void * other; /* valid when type == other */
} content;
} stack_data_t;
你仍然需要用某种方式来明确设置每个元素中存储的数据类型,没有简单的方法。
您可以依靠依赖于编译器的 typeof 关键字来查看预处理器魔术,以自动执行此操作,但这可能不会做任何事情,但会破坏可移植性。
答案 2 :(得分:1)
有些人建议void*
成员。除了该解决方案之外,我还想提供一种替代方案(假设您的堆栈是堆分配结构的链接列表):
struct stack_node
{
struct stack_node *next;
char data[];
};
data[]
是C99构造。 data
必须是最后一个成员;这利用了我们可以在结构地址之后填充任意数量的事实。如果您使用的是非C99编译器,则可能需要执行一些粗略的技巧,例如将其声明为data[0]
。
然后你可以这样做:
struct stack_node*
allocate_stack_node(size_t extra_size)
{
return malloc(sizeof(struct stack_node) + extra_size);
}
/* In some other function... */
struct stack_node *ptr = allocate_stack_node(sizeof(int));
int *p = (int*)ptr->data;
如果这看起来很丑陋和hacky,那就是......但是这里的优点是你仍然可以获得通用的好处,而不会引入更多的间接性(因此ptr->data
的访问时间比void*
稍快一些{{1}指向与结构不同的位置。)
更新:我还想指出,如果您的机器对int
的{{1}}的对齐要求不同于char
,则我提供的代码示例可能会出现问题。这是一个说明性的例子; YMMV。
答案 3 :(得分:0)
您可以使用宏和“容器”类型将“类型”从每个元素减少到整个容器。 (下面的C99代码)
#define GENERIC_STACK(name, type, typeid, elements) \
struct name##_stack { \
unsigned int TypeID; \
type Data[elements]; \
} name = { .TypeID = typeid }
当然,您的“TypeID”必须允许您期望的所有可能的约定类型;如果您打算使用整个结构或其他用户定义的类型,则可能会出现问题。
我意识到每个变量都有一个唯一命名的结构类型是奇数,可能没用... oops。
答案 4 :(得分:0)
我创建了一个适用于任何数据类型的库:
List new_list(int,int);
创建新列表,例如:
List list=new_list(TYPE_INT,sizeof(int));
//This will create an list of integers
Error append(List*,void*);
将一个元素附加到列表中。 *如果你想存储指向列表的指针,请追加两个指针作为参数,不要通过指针传递指针
例如:
//using the int list from above
int a=5;
Error err;
err=append(&list,&a)
//for an list of pointers
List listptr=new_list(TYPE_CUSTOM,sizeof(int*));
int num=7;
int *ptr=#
append(&listptr,ptr);
//for list of structs
struct Foo
{
int num;
float *ptr;
};
List list=new_list(TYPE_CUSTOM,sizeof(struct Foo));
struct Foo x;
x.num=9;
x.ptr=NULL;
append(&list,&x);
错误get(List *,int);
获取指定索引的数据。当被叫列表的当前指针将指向数据时。
例如:
List list=new_list(TYPE_INT,sizeof(int));
int i;
for(i=1;i<=10;i++)
append(&list,&i);
//This will print the element at index 2
get(&list,2);
printf("%d",*(int*)list.current);
错误弹出(List *,int);
来自指定索引的弹出和元素
例如:
List list=new_list(TYPE_INT,sizeof(int));
int i;
for(i=1;i<=10;i++)
append(&list,&i);
//element in the index 2 will be deleted,
//the current pointer will point to a location that has a copy of the data
pop(&list,2);
printf("%d",*(int*)list.current);
//To use the list as stack, pop at index list.len-1
pop(&list,list.len-1);
//To use the list as queue, pop at index 0
pop(&list,0);
错误合并(列表,列表);
合并两个相同类型的列表。如果类型不同,将在它返回的Error对象中返回错误消息;
例如:
//Merge two elements of type int
//List 2 will come after list 1
Error err;
err=merge(&list1,&list2);
Iterator get_iterator(List*);
获取列表的迭代器。初始化时将有一个指向列表第一个元素的指针。
例如:
Iterator ite=get_iterator(&list);
Error next(Iterator*);
获取列表的下一个元素。
例如:
//如何迭代整数列表
Iterator itr;
for(itr=get_iterator(&list); ite.content!=NULL; next(ite))
printf("%d",*(int*)ite.content);