<|im_start|>system
You are a helpful AI assistant.<|im_end|>
<|im_start|>user
Can you write a program in C to implement a stack data structure?<|im_end|>
<|im_start|>assistant
I can try! Keep in mind this code may not be totally up to date and may require modifications to suit your needs.

```C
#include <stdio.h>

#define MAX_SIZE 100

typedef struct {
    int arr[MAX_SIZE];
    int top;
} Stack;

void initialize(Stack *stack) {
    stack->top = -1;
}

int isEmpty(Stack *stack) {
    return (stack->top == -1);
}

int isFull(Stack *stack) {
    return (stack->top == MAX_SIZE - 1);
}

void push(Stack *stack, int data) {
    if (isFull(stack)) {
        printf("Stack overflow\n");
        return;
    }
    stack->arr[++stack->top] = data;
}

int pop(Stack *stack) {
    if (isEmpty(stack)) {
        printf("Stack underflow\n");
        return -1;
    }
    return stack->arr[stack->top--];
}

int peek(Stack *stack) {
    if (isEmpty(stack)) {
        printf("Stack is empty\n");
        return -1;
    }
    return stack->arr[stack->top];
}

int main() {
    Stack stack;
    initialize(&stack);

    push(&stack, 10);
    push(&stack, 20);
    push(&stack, 30);

    printf("Top element: %d\n", peek(&stack));

    printf("Popped element: %d\n", pop(&stack));
    printf("Popped element: %d\n", pop(&stack));

    printf("Top element: %d\n", peek(&stack));

    return 0;
}
```
In this program, we define a Stack structure with an array arr to hold the stack elements and top to keep track of the top element's index. The initialize function initializes the stack, isEmpty and isFull functions check if the stack is empty or full, respectively. The push function pushes an element onto the stack, pop function pops and returns the top element, and peek function returns the top element without removing it. The main function demonstrates the usage of these stack operations.

Note that this implementation assumes a fixed maximum size for the stack (MAX_SIZE in this case). If you need a dynamically resizable stack, consider using a linked list instead of an array.<|im_end|>
<|im_start|>user
Looks great!  I tried it and it runs fine, too.  But I think you're right, a dynamically resizeable stack would be better.  Can you show me how to do it with a linked list?  Write it just like before, that was perfect, just using linked lists this time.<|im_end|>
<|im_start|>assistant
Here is an implementation of a stack using a linked list in C:

```js
#include <stdio.h>
#include <stdlib.h>

struct Node {
  int data;
  struct Node* next;
};

struct Node* top = NULL; 

void push(int x) {

  struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
  newNode->data = x;
  
  newNode->next = top;
  top = newNode;
}

void pop() {
  if(top == NULL) {
    printf("Stack Underflow\n");
    return;
  }
  
  struct Node* temp = top;
  top = top->next;
  free(temp);
}

int peek() {

  if(top == NULL) {
    printf("Stack is empty\n");
    return -1;
  }
  return top->data;
}

int main() {

  push(1);
  push(2); 
  push(3);

  printf("Element at top of the stack: %d\n",peek());

  pop();

  printf("Element at top of the stack: %d\n",peek());
  
  return 0;
}
```


In this implementation, each node contains the data and a pointer to the next node. push() allocates a new node, sets its data, and makes it point to the previous top. pop() frees the node pointed to by top and moves top to the next node. peek() simply returns the data of the node pointed to by top.<|im_end|>
<|im_start|>user
Can you write a version that supports types other than int? I want to put whatever type I want there<|im_end|>
<|im_start|>assistant
Sure! We can create a generic stack data structure that can support any data type using void pointers in C. Here's the modified version of the stack implementation:

```c
#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 100

typedef struct {
    void* arr[MAX_SIZE];
    int top;
} Stack;

void initialize(Stack *stack) {
    stack->top = -1;
}

int isEmpty(Stack *stack) {
    return stack->top == -1;
}

int isFull(Stack *stack) {
    return stack->top == MAX_SIZE - 1;
}

void push(Stack *stack, void* data) {
    if (isFull(stack)) {
        printf("Stack Overflow: Cannot push element, stack is full.\n");
    } else {
        stack->arr[++stack->top] = data;
    }
}

void* pop(Stack *stack) {
    if (isEmpty(stack)) {
        printf("Stack Underflow: Cannot pop element, stack is empty.\n");
        return NULL; // Return NULL indicating failure
    } else {
        return stack->arr[stack->top--];
    }
}

void* peek(Stack *stack) {
    if (isEmpty(stack)) {
        printf("Stack is empty.\n");
        return NULL; // Return NULL indicating failure
    } else {
        return stack->arr[stack->top];
    }
}

// Example usage with int and double types
int main() {
    Stack stack;
    initialize(&stack);

    int num1 = 10;
    double num2 = 3.14;

    push(&stack, &num1);
    push(&stack, &num2);

    int* intPtr = (int*)peek(&stack);
    if (intPtr) {
        printf("Top element (int): %d\n", *intPtr);
    }

    double* doublePtr = (double*)pop(&stack);
    if (doublePtr) {
        printf("Popped element (double): %lf\n", *doublePtr);
    }

    intPtr = (int*)peek(&stack);
    if (intPtr) {
        printf("Top element (int): %d\n", *intPtr);
    }

    return 0;
}
```

In this version, we use `void*` pointers to store data of any type in the stack. This allows us to push and pop elements of various data types. The `main` function demonstrates how to use the stack with both `int` and `double` types. You can use the same stack data structure to store any other data type as needed.

Please note that when retrieving elements from the stack, you need to cast the `void*` pointer to the appropriate data type. This casting is necessary to correctly interpret the data stored in the stack. Also, since the stack is now generic, it's your responsibility to manage memory allocation and deallocation for elements appropriately.<|im_end|>