简体   繁体   English

数据类型独立堆栈 - C编程

[英]Datatype independent stack - C Programming

Often stacks in C are dependent upon datatype used to declare them. C中的堆栈通常依赖于用于声明它们的数据类型。 For example, 例如,

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

are both limited to holding integer and character datatypes respectively and presumes that the programmer knows what data is generated during the runtime. 两者都限于分别保存整数和字符数据类型,并假定程序员知道在运行时期间生成了什么数据。 What if I want a stack which can hold any datatype? 如果我想要一个可以容纳任何数据类型的堆栈怎么办?

I initially thought of implementing it as a union, but the approach is not only difficult but also flawed. 我最初想把它作为一个联盟来实现,但这种方法不仅困难而且有缺陷。 Any other suggestions? 还有其他建议吗?

I would use a structure like this: 我会使用这样的结构:

struct THolder
{
   int dataType; // this is a value representing the type
   void *val; // this is the value 
};

Then use an array of THolder to store your values. 然后使用THolder数组来存储您的值。

This is really just a variant of Pablo Santa Cruz' answer, but I think it looks neater: 这实际上只是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;

You still need to use some way to explicitly set the type of data stored in each element, there is no easy way around that. 您仍然需要使用某种方式来显式设置存储在每个元素中的数据类型,没有简单的方法。

You could look into preprocessor magic relying on the compiler-dependent typeof keyword to do that automagically, but that will probably not do anything but ruin the portability. 您可以依靠依赖于编译器的typeof关键字来查看预处理器魔术,以自动执行此操作,但这可能不会做任何事情,但会破坏可移植性。

Some people have suggested a void* member. 有些人建议使用void*会员。 In addition to that solution I'd like to offer an alternative (assuming your stack is a linked list of heap-allocated structures): 除了该解决方案之外,我还想提供一种替代方案(假设您的堆栈是堆分配结构的链接列表):

struct stack_node
{
   struct stack_node *next;
   char data[];
};

The data[] is a C99 construct. data[]是C99构造。 data must be the last member; data必须是最后一个成员; this takes advantage of the fact that we can stuff arbitrary quantities after the address of the struct. 这利用了我们可以在结构地址之后填充任意数量的事实。 If you're using non-C99 compiler you might have to do some sketchy trick like declare it as data[0] . 如果您使用的是非C99编译器,则可能需要执行一些粗略的技巧,例如将其声明为data[0]

Then you can do something like this: 然后你可以做这样的事情:

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;

If this looks ugly and hacky, it is... But the advantage here is that you still get the generic goodness without introducing more indirection (thus slightly quicker access times for ptr->data than if it were void* pointing to a different location from the structure.) 如果这看起来很丑陋,那就是......但是这里的优势在于你仍然可以获得通用的优点,而不会引入更多的间接性(因此ptr->data访问时间比void*指向不同的位置要快一些从结构。)

Update: I'd also like to point out that the code sample I give may have problems if your machine happens to have different alignment requirements for int than char . 更新:我也想指出的是,代码示例我给可能有问题,如果你的机器刚好有不同的对齐要求intchar This is meant as an illustrative example; 这是一个说明性的例子; YMMV. 因人而异。

I created an library that works for any data type: 我创建了一个适用于任何数据类型的库:

    List new_list(int,int);

creates new list eg: 创建新列表,例如:

    List list=new_list(TYPE_INT,sizeof(int));
    //This will create an list of integers
    Error append(List*,void*);

appends an element to the list. 将一个元素附加到列表中。 *Append accpts two pointers as an argument, if you want to store pointer to the list don't pass the pointer by pointer * Append将两个指针作为参数,如果要存储指向列表的指针,则不要通过指针传递指针

eg: 例如:

    //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);

Error get(List*,int); 错误get(List *,int);

Gets data at index specified. 获取指定索引处的数据。 When called list's current poiter will point to the data. 当被调用时,列表的当前指针将指向数据。

eg: 例如:

    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);

Error pop(List*,int); 错误弹出(List *,int);

Pops and element from the specified index 来自指定索引的Pops和元素

eg: 例如:

      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(List ,List); 错误合并(List,List);

Merges two list of same type. 合并两个相同类型的列表。 If types are different will return a error message in the Error object it returns; 如果类型不同,将在它返回的Error对象中返回错误消息;

eg: 例如:

     //Merge two elements of type int
     //List 2 will come after list 1
     Error err;
     err=merge(&list1,&list2);
    Iterator get_iterator(List*);

Get an iterator to an list. 获取列表的迭代器。 when initialized will have a pointer to the first element of the list. 初始化时将有一个指向列表第一个元素的指针。

eg: 例如:

    Iterator ite=get_iterator(&list);
    Error next(Iterator*);

Get the next element of the list. 获取列表的下一个元素。

eg: 例如:

//How to iterate an list of integers //如何迭代整数列表

      Iterator itr;
      for(itr=get_iterator(&list);  ite.content!=NULL;  next(ite))
        printf("%d",*(int*)ite.content);

https://github.com/malayh/C-List https://github.com/malayh/C-List

You could use macros and a "container" type to reduce "type" from being per-element, to whole-container. 您可以使用宏和“容器”类型将“类型”从每个元素减少到整个容器。 (C99 code below) (下面的C99代码)

#define GENERIC_STACK(name, type, typeid, elements) \
  struct name##_stack { \
    unsigned int TypeID; \
    type Data[elements]; \
  } name = { .TypeID = typeid }

Of course, your "TypeID" would have to allow every possible agreed-upon type you expect; 当然,您的“TypeID”必须允许您期望的每种可能的约定类型; might be a problem if you intend to use whole structs or other user-defined types. 如果您打算使用整个结构或其他用户定义的类型,可能会出现问题。

I realize having a uniquely named struct type for every variable is odd and probably not useful... oops. 我意识到每个变量都有一个唯一命名的结构类型是奇数,可能没有用... oops。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM