I have been assigned to program a generic stack in ANSI C. It is meant to be for primitive datatypes. Until here there was no big problem whatsoever.
Afterwards I was asked to reprogram my application so that even complex data types can be used on my stack. I have searched and researched for the last week and I found nothing that could be helpful enough.
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stddef.h>
#include "genstacklib.h"
void (*freefn) (void*);
/*
* ToDo
*/
void GenStackNew(genStack *s, int elemSize, void (*freefunk) (void*))
{
s->elems = malloc (elemSize * GenStackInitialAllocationSize);
freefn = freefunk;
assert (s->elems != NULL);
s->elemSize = elemSize;
s->logLength = 0;
s->allocLength = GenStackInitialAllocationSize;
}
/*
* ULStackPush adds an element to the stack and allocates new memory if
* needed. If there is not enough memory, ULStackPush does nothing.
*/
void GenStackPush (genStack *s, const void *elemAddr)
{
/*assert (sizeof(*elemAddr) == s->elemSize);*/
assert (s->elems != NULL);
if (s->logLength == s->allocLength)
{
void *temp = NULL;
temp = realloc (s->elems, 2 * s->allocLength * s->elemSize);
assert (temp != NULL);
s->allocLength = 2 * s->allocLength;
s->elems = temp;
}
memcpy(currentval(s), elemAddr, s->elemSize);
s->logLength = s->logLength + 1;
}
void GenStackPop (genStack *s, const void *elemAddr)
{
assert (s->elems != NULL);
assert (s->logLength != 0);
(s->logLength)--;
memcpy((void *)elemAddr, currentval(s), s->elemSize);
}
void *currentval(genStack *s)
{
assert (s->elems != NULL);
return ((size_t*)s->elems + s->logLength * s->elemSize);
}
bool GenStackEmpty (const genStack *s)
{
assert (s->elems != NULL);
return s->logLength == 0;
}
void GenStackDispose (genStack *s)
{
assert (s->elems != NULL);
s->logLength = 0;
free (s->elems);
freefn();
}
/*
* ToDO
*/
void *freefn (void *) {
free
And my header data is:
#ifndef GENSTACKLIB_H
#define GENSTACKLIB_H
#include <stdbool.h>
#define GenStackInitialAllocationSize 4
typedef struct
{
void *elems;
int elemSize;
int logLength;
int allocLength;
} genStack;
void GenStackNew (genStack * s, int elemSize);
bool GenStackEmpty (const genStack * s);
void GenStackPush (genStack * s, const void *elemAddr);
void GenStackPop (genStack * s, const void *elemAddr);
void GenStackDispose (genStack * s);
void *currentval(genStack *s);
#endif
In the first block of code, I believe that what has to be done is in the ToDo markings.
How can I make it to use my stack for complex data types?
Thanks in advance
I dont see any problem with "complex" types like strings... there is no real difference bewteen pointer to string and pointer to int. So just store pointers (or pointers to pointers) and that should work.
So instead of element to be "int".. element is pointer to pointer.
Basic idea in form of very "pseudo" C code
typedef struct Wrapper
{
void * primitiveData;
} Wrapper;
void PrimitivePush(void * data)
{
Wrapper * w = malloc();
w->primitiveData = malloc();
memcpy(w->primitiveData, data);
ClassicComplexTypePush(&w)
}
ClassicComplexTypePush(void ** data)
{
push data to stack
}
Consider using a singularly linked list for implementation, since when
using a stack, we don't know how many items may be needed.
Use a byte* or (char*) to store the contents of memory, instead of a void* (which would also work, but we may need to pad the allocation, to include structs)
Copy memory into a new allocation, which is pushed onto the stack,
then delete that used upon pop.
each node has to be of the same type, or at-least the same size,
errors using wrong type though may be undesired
pop can be either used to check if the stack is empty by passing (NULL)
or to actually pop the stack, by referencing the memory you want to set.
typedef unsigned char byte;
Create the structures which will be used to keep track of the stack
struct gStackNode {
byte *data;
struct gStackNode *next;
};
struct gStack {
unsigned size;
struct gStackNode *head;
};
Initialize the stack, including the size of the type we will be using
void stack_initalize(struct gStack *stk, unsigned size) {
if (!stk)
return;
stk->size = size;
stk->head = (void*)0;
}
Always, we need to manually free the stack, in-case not all were popped
void stack_free(struct gStack *stk) {
if (!stk)
return;
struct gStackNode *temp;
/* step through the remaining stack, deleting each item */
while(stk->head) {
temp = stk->head->next;
free((byte*)stk->head->data);
free((struct gStackNode *)stk->head);
stk->head = temp;
}
}
push an item onto the stack
void stack_push(struct gStack *stk, void *data) {
struct gStackNode *node = (struct gStackNode*)malloc(sizeof(struct gStackNode));
struct gStackNode *temp = stk->head;
node->next = temp;
node->data = (byte*)malloc(sizeof(byte)*(stk->size));
byte * src = (char*)(data);
byte * dest = (char*)(node->data);
unsigned n = stk->size;
/* fill the new allocation with source data */
for(;n;n--)
*(dest++) = *(src++);
/* the node becomes the new head */
stk->head = node;
}
Sometimes we don't want to use a local variable ie: stack_pop_(stack, &type) we can use stack_push_arg_no_ref(stack, 10).
void stack_push_arg_no_ref(struct gStack *stk, void *data) {
stack_push(stk, &data);
}
Now we can pop, and use the same to peek, passing (NULL) to data will result in a peek,
returning (1) if there is an item in the stack, and a (0) if its empty
int stack_pop(struct gStack *stk, void * data) {
if (!stk)
return 0;
if (!stk->head)
return 0;
if (data == (void*)0) {
/*
simply check to see if the stack is empty or not
don't actually pop the stack
*/
return ((!stk->head == (void*)0));
} else {
struct gStackNode *next = stk->head->next;
struct gStackNode *node = stk->head;
unsigned i;
byte *c_temp = (byte*)data;
for(i=0;i<stk->size;i++)
*c_temp++ = node->data[i];
free((byte*)node->data);
free((struct gStackNode*)node);
stk->head = next;
}
}
Finally we can implement the stack
using any ANSI C data types
the size of a character string needs to be fixed
structs can also be used
Using a character string
CAUTION, for this example, the strings need to be NULL terminated, though
it is possible to use non-NULL terminated strings
char ta[32] = "ta: text 1";
char tb[32] = "tb: text 2";
char tc[32];
struct gStack stack_char; stack_initalize(&stack_char, sizeof(ta));
stack_push(&stack_char, ta);
stack_push(&stack_char, tb);
while (stack_pop(&stack_char, &tc))
printf("%s\n", tc);
be sure to free the stack
stack_free(&stack_char);
Using integers
int a = 120, b = -32, c;
struct gStack stack_int; stack_initalize(&stack_int, sizeof(int));
stack_push(&stack_int, &a);
stack_push(&stack_int, &b);
/* or we can use */
stack_push_arg_no_ref(&stack_int, 1776);
/* we can now see the contents of the stack */
while (stack_pop(&stack_int, &c))
printf("%d\n", c);
stack_free(&stack_int);
Related
I was given this code from Uni and I am trying to do operations with it , the push function ask for two pointers and I could not figure out how can I pass the data to the item part.I have tried using another structure containing the data but I failed. I am stuck at the end of the code and I would like to learn how to push data using this code. How can I proceed ?
It would be preferable if the data itself was stored in a structur.
Thanks in advance.
typedef struct stk
{
struct stk* elems[MAX]; int top;
} stack, *stackptr;
void Init(stack* s)
{
s->top = 0;
}
int IsEmpty(stack s)
{
return (s.top == 0);
}
void Push(struct stk* item, stack* s)
{
if (s->top == MAX)
printf("Stack voll!");
s->elems[s->top] = item;
s->top++;
}
struct stk* Pop(stack* s)
{
if (IsEmpty(*s)) return NULL;
s->top--;
return s->elems[s->top];
}
int main()
{
stack* ptr = (stackptr)malloc(sizeof(stack));
Init(ptr);
printf("%d\n", ptr->top); // Ist 0 , OK
}
Here is a working stack implementation that store ints. This will afford you the opportunity to test that the operations work as expected. If you really want to store stack * replace the type. It seems unnecessarily confusing for a entry level class to have an assignment of storing pointers to the same thing you are building.
When you deal with pointers you want to make sure the object they point to outline the pointer. You may also want to think of shallow and deep copies with pointers. If you Pop followed by a Push the pointer that was returned from Pop now will point to the new value which would be surprising. Consider a different designs:
Pass in a reference to a variable (aka out parameter) so Pop(stack *s, *v) (and use an enum or define constants for error values).
return a value instead of a pointer; error would not be an out parameter.
return a pointer to a copy of the value and require client to free it.
#include <stdio.h>
#include <stdlib.h>
#define MAX 10
typedef struct stack {
int elems[MAX];
int top;
} stack;
void Init(stack *s) {
if(!s)
return;
s->top = 0;
}
int IsEmpty(stack *s) {
return (s->top == 0);
}
void Push(stack *s, int elem) {
if (s->top == MAX) {
printf("Stack voll!");
return;
}
s->elems[s->top++] = elem;
}
int *Pop(stack *s) {
if (IsEmpty(s))
return NULL;
return &s->elems[--(s->top)];
}
int main() {
stack *s = malloc(sizeof *s);
Init(s);
printf("%d\n", s->top); // Ist 0 , OK
Push(s, 42);
int *v = Pop(s);
printf("%d\n", *v);
}
and example run:
0
42
Consider using a name prefix like "Stack" for all your symbols to avoid name conflicts.
In c we don't cast void * (from malloc()).
I get a segfault while runnig this code to implement a stack in C. Please note that the code is kind of incomplete. I just wanted to check and see if I could push a few elements on to the stack and print them out. But it throws back a segfault. Any help would be much appreciated!!
#include<stdlib.h>
#include<stdio.h>
struct stack
{
int *elems;
int ll;
int al;
};
void stack_new(struct stack *s)
{
s->ll=0;
s->al=4;
s->elems=malloc(4*sizeof(int));
}
void stack_del(struct stack *s)
{
free(s->elems);
}
void stack_push(struct stack *s,int value)
{
if(s->ll==s->al)
{
printf("overflow");
/*s->al*=2;
s->elems=realloc(s->elems, s->al*sizeof(int));*/
}
s->elems[s->ll]=value;
s->ll++;
}
void stack_pop(struct stack *s)
{
s->ll--;
return (s->elems[s->ll]);
}
void main()
{
struct stack *s;
stack_new(s);
stack_push(s,3);
stack_push(s,4);
stack_push(s,8);
printf("%d", s->elems[0]);
//stack_pop(s);
//stack_del(s);
}
Declaring
struct stack *s;
doesn’t allocate any memory for a struct stack. Do that:
struct stack *s = malloc(sizeof *s);
Or just put your stack on the stack:
struct stack s;
stack_new(&s);
…
Using more descriptive field names is also a good idea.
You have several errors
You never initialize the pointer s in your main function, so in your stack_new function dereferencing s causes a segmentation fault.
You should allocate space for the stack first, wherever you want but you must.
Another thing is if you want to initialize your al field with a constant number and then allocate an array of constant size, you don't need the field al, and you can declare elems as int elems[CONSTANT_NUMBER] but if you want it to be dynamic, which is what I think you want from your check if(s->ll == s->al) in the stack_push function, then you can simply pass the value you want al to have to the stack_new function.
This is some of your code, fixed so you can see what I actually mean.
#include<stdlib.h>
#include<stdio.h>
struct stack
{
int *elems;
int ll;
int al;
};
struct stack *stack_new(int al) /* you can pass the maximum number of elements allowed */
{
struct stack *s;
s = malloc(sizeof(struct stack));
if (s == NULL)
return NULL;
s->ll = 0;
s->al = al;
s->elems = malloc(al * sizeof(int)); /* and you dynamically allocate space for them here */
return s;
}
void stack_del(struct stack *s)
{
if (s != NULL) /* always check the pointers to prevent `SEGMENTATION FAULT` */
{
if (s->elems != NULL)
free(s->elems);
free(s);
}
}
void stack_push(struct stack *s, int value)
{
if (s == NULL)
return;
if(s->ll == s->al)
{
printf("overflow");
/*s->al*=2;
s->elems=realloc(s->elems, s->al*sizeof(int));*/
}
if (s->elems != NULL)
s->elems[s->ll] = value;
s->ll++;
}
int stack_pop(struct stack *s)
{
if ((s == NULL) || (s->elems == NULL))
return 0;
s->ll--;
return (s->elems[s->ll]);
}
int main()
{
struct stack *s;
s = stack_new(4);
stack_push(s, 3);
stack_push(s, 4);
stack_push(s, 8);
printf("%d", s->elems[0]);
stack_pop(s);
stack_del(s);
return 0;
}
```
I am trying to traverse a binary tree using twalk() with <search.h>
#define _GNU_SOURCE /* Expose declaration of tdestroy() */
#include <search.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
void *root = NULL;
void *
xmalloc(unsigned n)
{
void *p;
p = malloc(n);
if (p)
return p;
fprintf(stderr, "insufficient memory\n");
exit(EXIT_FAILURE);
}
int
compare(const void *pa, const void *pb)
{
if (*(int *) pa < *(int *) pb)
return -1;
if (*(int *) pa > *(int *) pb)
return 1;
return 0;
}
void
action(const void *nodep, const VISIT which, const int depth)
{
int *datap;
switch (which) {
case preorder:
break;
case postorder:
datap = *(int **) nodep;
printf("%6d\n", *datap);
break;
case endorder:
break;
case leaf:
datap = *(int **) nodep;
printf("%6d\n", *datap);
break;
}
}
int
main(void)
{
int i, *ptr;
void *val;
srand(time(NULL));
for (i = 0; i < 12; i++) {
ptr = (int *) xmalloc(sizeof(int));
*ptr = rand() & 0xff;
val = tsearch((void *) ptr, &root, compare);
if (val == NULL)
exit(EXIT_FAILURE);
else if ((*(int **) val) != ptr)
free(ptr);
}
twalk(root, action);
tdestroy(root, free);
exit(EXIT_SUCCESS);
}
As you can see there is no way to pass to or return any variable from action().
why is so hermetic? I can't use any global because the program uses threads, my question is: how can I traverse (and share nodep with non-global variable) in thread-safe mode?
Excuse my poor english
EDIT:
As unwind said, the solution is to re-invent this particular wheel, redefine the structure used at tsearch.c solves the problem:
/* twalk() fake */
struct node_t
{
const void *key;
struct node_t *left;
struct node_t *right;
unsigned int red:1;
};
static void tmycallback(const xdata *data, const void *misc)
{
printf("%s %s\n", (const char *)misc, data->value);
}
static void tmywalk(const struct node_t *root, void (*callback)(const xdata *, const void *), const void *misc)
{
if (root->left == NULL && root->right == NULL) {
callback(*(xdata * const *)root, misc);
} else {
if (root->left != NULL) tmywalk(root->left, callback, misc);
callback(*(xdata * const *)root, misc);
if (root->right != NULL) tmywalk(root->right, callback, misc);
}
}
/* END twalk() fake */
if (root) tmywalk(root, tmycallback, "Hello walker");
I guess nobody can answer the "why" exactly, except those who specified and implemented the functions. I guess "shortsightedness", or maybe "historical reasons" (they did it before thread programming became a common thing).
Anyway, this API seems a bit "toyish" to me due to this limitation, as do in fact all APIs that fail to include a user-owned void * that is just opaquely passed around between API and any callbacks.
So, the solution I guess is to re-invent this particular wheel, and write your own functions to traverse a binary tree.
You can use thread-local storage to be able to use a global variable and still be thread-safe. Apparently you can use the __thread keyword for this purpose. Also check Using __thread in c99.
I'm trying to implement a simple stack. It works fine on MacOSX but crashes on linux(Ubuntu). Can someone help me to know why?
MYStack.h
#ifndef MYSTACK_H_
#define MYSTACK_H_
typedef struct Element *element;
typedef struct Stack *stack;
struct Element {
struct Element *next;
void *data;
};
struct Stack {
struct Element *head;
unsigned int count;
void (*dump) (void *);
};
/* Define boolean type */
typedef signed char bool;
#define YES (bool)1
#define NO (bool)0
/* utility macro */
#define ELEMENT_NEXT(E) ((E) = (E)->next)
#define ELEMENT_DATA(E) ((E)->data)
#define STACK_HEAD(S) ((S)->head)
#define STACK_SIZE(S) ((S)->count)
/* Functions prototypes */
bool push( stack , void * );
bool pop( stack , void ** );
bool create_stack( stack , void (*) (void*) );
bool delete_stack( stack );
void dump_stack( stack );
#endif /* MYSTACK_H_ */
MYStack.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "MYStack.h"
/* Creating the stack */
bool create_stack( stack new_stack, void (*dump_function) (void*))
{
new_stack->head = NULL;
new_stack->dump = dump_function;
new_stack->count = 0;
return YES;
}
/* Deleting the stack */
bool delete_stack( stack this_stack )
{
element next_element;
while (this_stack->head) {
next_element = this_stack->head->next;
free(this_stack->head->next);
this_stack->head = next_element;
}
return YES;
}
/* Dump the stack */
void dump_stack(stack this_stack)
{
element e;
int i;
e = STACK_HEAD(this_stack);
if (this_stack->dump ) {
for (i = 0; i < this_stack->count; i++) {
(this_stack->dump) (e->data);
if (e->next != NULL)
e = e->next;
}
}
}
/* Adding element to the stack */
bool push( stack this_stack, void *data )
{
element new_element;
void (*temp_dump) (void *);
this_stack->dump = temp_dump;
new_element = (element ) malloc(sizeof(element *));
if (new_element == NULL) {
fprintf(stdout, "malloc() new element failed : %d\n", errno);
return NO;
}
new_element->data = data;
new_element->next = (this_stack)->head;
(this_stack)->head = new_element;
this_stack->dump = temp_dump;
(this_stack)->count++;
#ifdef DEBUG
fprintf(stdout, "Inserting at the stack in the address %p\n", new_element->data);
#endif
return YES;
}
/* Remving element from the stack */
bool pop( stack this_stack, void **data )
{
element delete_me;
void (*temp_dump) (void *);
this_stack->dump = temp_dump;
delete_me = this_stack->head;
if (delete_me == NULL) {
fprintf(stdout, "stack is empty\n");
return NO;
}
*data = delete_me->data;
this_stack->head = delete_me->next;
this_stack->dump = temp_dump;
this_stack->count--;
free(delete_me);
#ifdef DEBUG
fprintf(stdout, "Removing from the stack in the address %p\n", delete_me->data);
#endif
return YES;
}
/* Dump function test */
void dump_ints(void* data)
{
int* int_data_ptr = (int*)data;
printf("We are dumping an int data : %d\n", *int_data_ptr);
}
/* To test our stack */
int main (int argc, char const **argv)
{
stack new_stack;
void (*dump_func_ptr) (void*);
int i;
int stack_ints[] = {1, 2, 3, 4};
void *deleted_item;
#ifdef DEBUG
fprintf(stdout, "We are in debug mode...\n");
#endif
new_stack = (stack) malloc(sizeof(stack *));
dump_func_ptr = dump_ints;
create_stack( new_stack, dump_func_ptr);
/* Insert to the stack */
for (i = 0; i < 4; i++)
push( new_stack, &stack_ints[i]);
/* Print the number of elements */
printf("Our stack contain %d elements\n", new_stack->count);
/* Dump the stack */
dump_stack(new_stack);
/* Removing some data */
pop(new_stack, &deleted_item);
/* Dump the stack */
dump_stack(new_stack);
/* Deleting the stack */
//delete_stack(new_stack);
free(new_stack);
return 0;
}
There are a couple of incorrect allocations. They are only allocating memory the size of a pointer (4 bytes on a 32-bit system) and subsequent initialization of those pieces of memory will overwrite past the end of the allocated memory. The ones I noticed are these:
new_element = (element ) malloc(sizeof(element *));
and
new_stack = (stack) malloc(sizeof(stack *));
They should be something like this:
new_element = (element ) malloc(sizeof(struct Element));
new_stack = (stack) malloc(sizeof(struct Stack));
next_element = this_stack->head->next;
free(this_stack->head->next);
this_stack->head = next_element;
On the next iteration of the loop, you access this_stack->head, which has just been freed.
I want to have a stack that takes strings. I want to be able to push and pop strings off, as well as clear the whole stack. I think C++ has some methods for this. What about C?
Quick-and-dirty untested example. Uses a singly-linked list structure; elements are pushed onto and popped from the head of the list.
#include <stdlib.h>
#include <string.h>
/**
* Type for individual stack entry
*/
struct stack_entry {
char *data;
struct stack_entry *next;
}
/**
* Type for stack instance
*/
struct stack_t
{
struct stack_entry *head;
size_t stackSize; // not strictly necessary, but
// useful for logging
}
/**
* Create a new stack instance
*/
struct stack_t *newStack(void)
{
struct stack_t *stack = malloc(sizeof *stack);
if (stack)
{
stack->head = NULL;
stack->stackSize = 0;
}
return stack;
}
/**
* Make a copy of the string to be stored (assumes
* strdup() or similar functionality is not
* available
*/
char *copyString(char *str)
{
char *tmp = malloc(strlen(str) + 1);
if (tmp)
strcpy(tmp, str);
return tmp;
}
/**
* Push a value onto the stack
*/
void push(struct stack_t *theStack, char *value)
{
struct stack_entry *entry = malloc(sizeof *entry);
if (entry)
{
entry->data = copyString(value);
entry->next = theStack->head;
theStack->head = entry;
theStack->stackSize++;
}
else
{
// handle error here
}
}
/**
* Get the value at the top of the stack
*/
char *top(struct stack_t *theStack)
{
if (theStack && theStack->head)
return theStack->head->data;
else
return NULL;
}
/**
* Pop the top element from the stack; this deletes both
* the stack entry and the string it points to
*/
void pop(struct stack_t *theStack)
{
if (theStack->head != NULL)
{
struct stack_entry *tmp = theStack->head;
theStack->head = theStack->head->next;
free(tmp->data);
free(tmp);
theStack->stackSize--;
}
}
/**
* Clear all elements from the stack
*/
void clear (struct stack_t *theStack)
{
while (theStack->head != NULL)
pop(theStack);
}
/**
* Destroy a stack instance
*/
void destroyStack(struct stack_t **theStack)
{
clear(*theStack);
free(*theStack);
*theStack = NULL;
}
Edit
It would help to have an example of how to use it:
int main(void)
{
struct stack_t *theStack = newStack();
char *data;
push(theStack, "foo");
push(theStack, "bar");
...
data = top(theStack);
pop(theStack);
...
clear(theStack);
destroyStack(&theStack);
...
}
You can declare stacks as auto variables, rather than using newStack() and destroyStack(), you just need to make sure they're initialzed properly, as in
int main(void)
{
struct stack_t myStack = {NULL, 0};
push (&myStack, "this is a test");
push (&myStack, "this is another test");
...
clear(&myStack);
}
I'm just in the habit of creating pseudo constructors/destructors for everything.
Try GNU Obstacks.
From Wikipedia:
In the C programming language, Obstack is a memory-management GNU extension to the C standard library. An "obstack" is a "stack" of "objects" (data items) which is dynamically managed.
Code example from Wikipedia:
char *x;
void *(*funcp)();
x = (char *) obstack_alloc(obptr, size); /* Use the macro. */
x = (char *) (obstack_alloc) (obptr, size); /* Call the function. */
funcp = obstack_alloc; /* Take the address of the function. */
IMO what makes Obstacks special: It does not need malloc() nor free(), but the memory still can be allocated «dynamically». It is like alloca() on steroids. It is also available on many platforms, since it is a part of the GNU C Library. Especially on embedded systems it might make more sense to use Obstacks instead of malloc().
See Wikipedia's article about stacks.