3

Often stacks in C are dependent upon datatype used to declare them. 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?

5
  • Are you sure you're not confusing stack (a data structure) with variables allocated on stack? Commented Feb 21, 2011 at 11:03
  • No not at all. I want a stack on which I can push anything with out knowing its type beforehand. Commented Feb 21, 2011 at 11:16
  • You need to explain more what you want. Commented Feb 21, 2011 at 11:29
  • It's fairly easy (though ugly) to create a stack type that will handle objects of the same type where that type isn't known until runtime. It's somewhat more difficult to create a stack that can hold objects of more than one type, and AFAIK there's no good use case for such a structure. How would you expect your pop operation to work in that case? Commented Feb 21, 2011 at 12:36
  • Well, the general opinion suggests that it is quite difficult to implement such a stack at least in C. Let me see if I can get a work around. Thanks everyone! Commented Feb 22, 2011 at 14:17

5 Answers 5

4

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.

Sign up to request clarification or add additional context in comments.

8 Comments

Again this one requires knowing of the datatype to be pushed before hand.
@check123: yes, but you need that extra data, otherwise, when you access the array you won't know that to de-reference. Remember: you are talking about C, not C++. There are no TEMPLATES in C. There is no inheritance (which might have helped if you were willing to compromise by inheriting all your classes from a single base class).
@check123 You always know the type of the thing you're pushing. If these suggestions aren't adequate, you need to more state your requirements more clearly.
If the code module doing the pushing isn't doing the popping as well, I'd suspect that the program design is flawed.
@Jim I think you misunderstood what I meant. With a "code module" I mean a bunch of routines belonging together, ie the way you do OO programming in C with no class keyword. To take your interpreter as an example: you could have one module handling user input and one module handling expression evaluation. Good program design would be to have the expression evaluation module handling both pushing and popping, keeping the stack object as a "private" variable. Bad program design would be to have a global stack object, then let the user interface push objects and the evaluation module pop them.
|
2

This is really just a variant of Pablo Santa Cruz' answer, but I think it looks neater:

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.

4 Comments

This is known as a "variant". They are known as a terrible waste of memory space, with no apparent advantages.
struct and linked lists were used all above is a memory waste. type selector is an optional field, remove it if you warn about single byte per element. this answer is the only one right answer.
the next step is making a block-allocated array of these elements, with a size closer to memory allocation unit size (multiple of 4K on x86), with all array blocks linked in a list -- that's the classical dynamic stack
the advantage of this structure is a single machine command indexed access if the whole stack uses the single statically-allocated contiguous memory region
1

Some people have suggested a void* member. 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 must be the last member; 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].

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

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. This is meant as an illustrative example; YMMV.

5 Comments

You added dynamic memory allocation and then claim that your code will be quicker?
@Lundin - Sigh. This fits the trend of people on Stack Overflow taking suggestions far too literally. I am merely trying to make the questioner aware of possibilities, not promise which one is best in all cases. Note I say: "assuming your stack is a linked list of heap-allocated structures". If you're already doing heap allocations for your structure then yes, this is fewer pointer dereferences and should be faster. But that "if" might not suit your scenario.
Fair enough, but the code is still icky... A struct may contain any number of padding bytes (and for this case it likely will, on most platforms) and your code will overwrite those padding bytes with data. Even if such suspicious code doesn't cause the program to crash, you are still allocating more data than needed. Strictly speaking you should be allocating sizeof(stack_node*) + extra_size.
Or offsetof(struct stack_node, data) + extra_size.
Its a linked list with one fixed typed char[] element, not a heterogeneous stack was asked
0

You could use macros and a "container" type to reduce "type" from being per-element, to whole-container. (C99 code below)

#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; 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.

1 Comment

So the question still remain open!
0

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

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

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

Pops and element from the specified index

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

Merges two list of same type. If types are different will return a error message in the Error object it returns;

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.