I'm trying to do a "class model" in C in which I define a struct that represents the class and inside it I define function pointers to represent methods, like this:
//ClassName.h
typedef struct struct_ClassName ClassName;
struct ClassName
{
char a;
char b;
char c;
void (*method1)(ClassName*, char);
void (*method2)(ClassName*, char);
...
void (*methodN)(ClassName*, char);
};
void initClassName(ClassName*);
//ClassName.c
#include "ClassName.h"
static void method1(ClassName *this_c, char c);
static void method2(ClassName *this_c, char c);
...
static void methodN(ClassName *this_c, char c);
void initClassName(ClassName *this_c)
{
this_c->method1 = &method1;
this_c->method2 = &method2;
...
this_c->methodN = &methodN;
}
void method1(ClassName *this_c, char c)
{
//do something
}
void method2(ClassName *this_c, char c)
{
//do something
}
...
void methodN(ClassName *this_c, char c)
{
//do something
}
Everything works fine but, somewhere in the code, I define an array:
...
ClassName objects[200];
for(i = 0; i < 200; i++)
{
initClassName(&objects[i]);
}
...
Because of the function pointers, the memory usage associated to this array is quite high (I have several "methods" in this "class").
Considering that this is a code that will run in an embedded system, is there a better way to do it?
Defining functions outside the structure could be a possibility but in my opinion it does not respect what I'm trying to emulate.
I have to use only C, not C++.
What you have created is a very dynamic system where each object instance can have its own set of method implementations. That's why it uses so much memory.
Another approach is an implementation closer to early C++ (before multiple inheritance) where all instance of the same class share the same vtable. The vtable contains the function pointers.
typedef struct struct_ClassName ClassName;
typedef struct struct_ClassName_vtable ClassName_vtable;
struct ClassName_vtable
{
void (*method1)(ClassName*, char);
void (*method2)(ClassName*, char);
...
void (*methodN)(ClassName*, char);
}
struct ClassName
{
ClassName_vtable* _vtable;
char a;
char b;
char c;
};
void initClassName(ClassName*);
static void method1(ClassName *this_c, char c);
static void method2(ClassName *this_c, char c);
...
static void methodN(ClassName *this_c, char c);
ClassName_vtable _ClassName_vtable = {
method1,
method2,
...,
methodN
};
void initClassName(ClassName *this_c)
{
this_c->_vtable = _ClassName_vtable;
}
That way, the OO overhead per instance is only the size of a pointer. It's also easier to create subclasses.
A method call looks like this:
ClassName* obj = ...;
obj->vtable->method2(obj, 'a');
ClassName objects[200];
for(i = 0; i < 200; i++)
initClassName(&objects[i]);
I will show you a stripped-off version of something I use here
for similar effect. It is hard to say when a certain size is huge in terms of pointers or whatever. Each environment has their truth and this can be useless or useful...
Anyway, the ideia is encapsulation. And in C we have no this pointer. C is not C++ or java or javascript. Each class instance must have a table of function pointers for the methods. We need to build these tables. This is how virtual classes are implemented in others languages anyway. And if each class element can allocate memory code is needed to allocate and free memory.
TL;DR
Below is a C example of a program that builds and uses an array of classes. In this case an array of stacks. It shows a mechanism of building stacks of mixed things. Each array item has all that is needed to manage his own stack instance, be it of a trivial type or a complex structure. It can be easily changed to implement other tyoes of classes in C.
Please do not bother warning me that I cast the return of malloc(). I , as many others, do not like implicit things. And I know that C-FAQ is a never-updated thing from the end of the '90s so no need to reference this either.
An example: a static STACK container
typedef struct
{
int data[SIZE];
int limit; // capacity
int size; // actual
} Stack;
This is it: a simple stack of int values. Let us say we want to declare a vector of stacks of different things, but in C. And use methods on them. If we use trivial types --- in C++ we say the struct is trivialy constructible --- things can get easier, but if we are about to use structs we need to know about how to manipulate stack elements, since they can allocate memory.
We are writing a container so the methods of the class must work for any underlying data. And we have no iterators like C++ STL. Here we are implementing the POP TOP and PUSH methods for stacks, and a toString() method like in java to print values on the screen.
For each possible content in the container we need to have a constructor, a
destructor, a copy constructor and a display method. In this example we have just 2 types of stacks: a stack of int and a stack of struct Sample:
typedef struct
{
size_t _id;
char name[30];
char phone[20];
} Sample;
We can add others just by writing the required 4 functions.
main.c example
int main(void)
{
srand(220804); // for the factory functions
Stack* class_array[2] = {
create(4, create_i, copy_i, destroy_i, show_i),
create(3, create_st, copy_st, destroy_st, show_st)};
printf("\n\n=====> Testing with STACK of int\n\n");
class_test(class_array[0], factory_i);
printf(
"\n\n=====> Testing with STACK of struct "
"Sample\n\n");
class_test(class_array[1], factory_st);
class_array[0]->destroy(class_array[0]);
class_array[1]->destroy(class_array[1]);
return 0;
}
Each instance of Stack has pointers to the stack methods and to the functions that manipulate the stack data, so we can have a single class_test() function that does the following:
builds a stack of the required size, 4 or 3 in the example
fills the stack with data generated by factory functions (in production the logic builds the data)
shows the stack contents
removes all stack elements, one by one
At the end the destructor is called for eack stack.
The class.h file
typedef void* (PVFV)(void*);
typedef int (PIFV)(void*);
typedef struct
{
size_t size_;
size_t lim_;
void** data_;
PVFV* copy;
PVFV* destroy;
int (*show)(void*,const char*); // for testing
// constructor and destructor for container elements
PVFV* create_1;
PVFV* copy_1;
PVFV* destroy_1;
PIFV* show_1;
// member functions
PIFV* POP;
int (*PUSH)(void*,void*);
PVFV* TOP;
PIFV* empty;
size_t (*size)(void*);
} Stack;
Stack* create(
size_t,
void* (*)(void*),
void* (*)(void*),
void* (*)(void*),
int (*)(void*));
int class_test(Stack*, void* (*)());
the example output
=====> Testing with STACK of int
Stack is empty
POP() on empty stack returned -2
TOP() on empty stack returned NULL
Calls PUSH until error
Value inserted: 42
Value inserted: 41
Value inserted: 40
Value inserted: 39
Stack now has 4 elements
Stack has 4 of 4 elements:
42
41
40
39
Calls POP() until error
Stack size: 3
Stack size: 2
Stack size: 1
Stack size: 0
=====> Testing with STACK of struct Sample
Stack is empty
POP() on empty stack returned -2
TOP() on empty stack returned NULL
Calls PUSH until error
Value inserted: 0195 Sample id#0195 +76(203)6840-195
Value inserted: 0943 Sample id#0943 +35(686)9368-943
Value inserted: 0152 Sample id#0152 +16(051)8816-152
Stack now has 3 elements
Stack has 3 of 3 elements:
0096 Sample id#0096 +24(477)0418-096
0037 Sample id#0037 +27(214)3509-037
0836 Sample id#0836 +68(857)4634-836
Calls POP() until error
Stack size: 2
Stack size: 1
Stack size: 0
the logic
For each tye of element we need to write the 4 functions: they can alocate memory and be very complex or they can be trivial, but the class methods need to handle any case.
code for struct Sample in stack_struct.h###
#pragma once
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
void* copy_st(void*);
void* create_st(void*);
void* destroy_st(void*);
void* factory_st();
typedef struct
{
size_t _id;
char name[30];
char phone[20];
} Sample;
void* create_st(void* el)
{
return factory_st();
}
void* copy_st(void* el)
{
if (el == NULL) return NULL;
Sample* e = (Sample*)malloc(sizeof(Sample));
*e = *((Sample*)el);
return e;
}
void* destroy_st(void* el)
{
if (el == NULL) return NULL;
free(el);
return NULL;
}
int show_st(void* el)
{
if (el == NULL) return 0;
Sample* e = (Sample*)el;
printf(
" %04d %15s %20s\n",
(int) e->_id, e->name, e->phone);
return 0;
}
void* factory_st()
{
Sample* e = (Sample*)malloc(sizeof(Sample));
e->_id = rand() % 1000;
sprintf(e->name, "Sample id#%04d", (int)e->_id);
memset(e->phone, 0, sizeof(e->phone));
e->phone[0] = '+';
for (int i = 1; i <= 17; i += 1)
e->phone[i] = '0' + rand() % 10;
e->phone[3] = '(';
e->phone[7] = ')';
e->phone[12] = '-';
e->phone[13] = e->name[11];
e->phone[14] = e->name[12];
e->phone[15] = e->name[13];
e->phone[16] = e->name[14];
e->phone[17] = 0;
return (void*)e;
}
code for int elements stack_int.h_###
#pragma once
#include <stdio.h>
#include <stdlib.h>
void* create_i(void* el)
{
int* e = (int*)malloc(sizeof(int));
*e = *((int*)el);
return (void*)e;
}
void* copy_i(void* el)
{
if (el == NULL) return NULL;
int* e = (int*)malloc(sizeof(int));
*e = *( (int*)el );
return e;
}
void* destroy_i(void* el)
{
if (el == NULL) return NULL;
free(el);
return NULL;
}
int show_i(void* el)
{
if (el == NULL) return 0;
int v = *((int*)el);
printf(" %d\n", v);
return 0;
}
void* factory_i()
{
static int i = 42;
int* new_int = (int*)malloc(sizeof(int));
*new_int = i;
i -= 1;
return (void*)new_int;
}
The class implementation class.c
#include "class.h"
#include <stdio.h>
#include <stdlib.h>
void* Copy__(void*);
void* Destroy__(void*);
int POP__(void*);
int PUSH__(void*, void*);
int Show__(void*, const char*);
void* TOP__(void*);
int empty__(void*);
size_t size__(void*);
Stack* create(
size_t sz, void* (*create)(void*), void* (*copy)(void*),
void* (*destroy)(void*), int (*show)(void*))
{
Stack* stack = (Stack*)malloc(sizeof(Stack));
if (stack == NULL) return NULL;
stack->size_ = 0;
stack->lim_ = sz;
stack->data_ = (void*)malloc(sz * sizeof(void*));
stack->copy = Copy__;
stack->destroy = Destroy__;
stack->show = Show__;
stack->create_1 = create;
stack->copy_1 = copy;
stack->destroy_1 = destroy;
stack->show_1 = show;
stack->POP = POP__;
stack->PUSH = PUSH__;
stack->TOP = TOP__;
stack->empty = empty__;
stack->size = size__;
return stack;
}
void* Copy__(void* one) { return NULL; };
void* Destroy__(void* stack)
{ // before destructing a stack we need to
// destroy all elements
if (stack == NULL) return NULL;
Stack* st = (Stack*)stack;
for (size_t ix = 0; ix < st->size_; ix += 1)
(st->destroy_1)(st->data_[ix]);
free(st->data_);
free(st);
return NULL;
};
int POP__(void* stack)
{
if (stack == NULL) return -1; // no stack
Stack* st = stack;
if (st->size_ == 0) return -2; // empty
st->size_ -= 1; // one less
return 0; // ok
}
int PUSH__(void* el, void* stack)
{
if (el == NULL) return -1; // no element
if (stack == NULL) return -2; // no stack
Stack* st = (Stack*)stack;
if (st->size_ == st->lim_) return -3; // full
void* new_el = st->create_1(el); // copy construct
st->data_[st->size_] = new_el;
st->size_ += 1; // one up
return 0; // ok
}
int Show__(void* stack, const char* title)
{
if (stack == NULL) return -1;
Stack* st = stack;
if (title != NULL) printf("%s\n", title);
if (st->size_ == 0)
{
printf("Stack is empty\n");
return 0;
}
for (size_t ix = 0; ix < st->size_; ix += 1)
st->show_1(st->data_[ix]);
printf("\n");
return 0;
}
void* TOP__(void* stack)
{
if (stack == NULL) return NULL; // no stack
Stack* st = stack;
if (st->size_ == 0) return NULL; // empty
return st->data_[st->size_ - 1]; // ok
}
int empty__(void* stack)
{
if (stack == NULL) return 1; // empty??
return ((Stack*)stack)->size_ == 0;
}
size_t size__(void* stack)
{
if (stack == NULL) return 1; // empty??
return ((Stack*)stack)->size_;
}
///////////// TEST FUNCTION ///////////////
int class_test(Stack* tst, void* (*factory)())
{
if (tst == NULL) return -1;
// is stack empty?
if (tst->empty(tst))
printf("Stack is empty\n");
else
printf("Stack: %zd elements\n", tst->size(tst));
int res = tst->POP(tst);
printf("POP() on empty stack returned %d\n", res);
void* top = tst->TOP(tst);
if (top == NULL)
printf("TOP() on empty stack returned NULL\n");
else
{
printf(
"\nTOP() on empty stack returned NOT NULL!\n");
return -2;
}
printf("Calls PUSH until error\n\n");
void* one = factory();
int value = *(int*)one;
while (tst->PUSH(one, tst) == 0)
{
printf("Value inserted:");
tst->show_1(one);
free(one);
one = factory();
}
free(one); // last one, not inserted
printf("Stack now has %zd elements\n", tst->size(tst));
char title[80] = {" "};
sprintf(
title, "\nStack has %zd of %zd elements:\n",
tst->size_, tst->lim_);
tst->show(tst, title);
// agora esvazia a pilha ate dar erro
printf("\nCalls POP() until error\n");
while (tst->POP(tst) == 0)
printf("Stack size: %I32d\n", (int)tst->size(tst));
return 0;
};
The complete main.c program
#include <stdio.h>
#include "class.h"
#include "stack_int.h"
#include "stack_struct.h"
int main(void)
{
srand(220804);
Stack* class_array[2] = {
create(4, create_i, copy_i, destroy_i, show_i),
create(3, create_st, copy_st, destroy_st, show_st)};
printf("\n\n=====> Testing with STACK of int\n\n");
class_test(class_array[0], factory_i);
printf(
"\n\n=====> Testing with STACK of struct "
"Sample\n\n");
class_test(class_array[1], factory_st);
class_array[0]->destroy(class_array[0]);
class_array[1]->destroy(class_array[1]);
return 0;
}
Related
I'm trying to do a "class model" in C in which I define a struct that represents the class and inside it I define function pointers to represent methods, like this:
//ClassName.h
typedef struct struct_ClassName ClassName;
struct ClassName
{
char a;
char b;
char c;
void (*method1)(ClassName*, char);
void (*method2)(ClassName*, char);
...
void (*methodN)(ClassName*, char);
};
void initClassName(ClassName*);
//ClassName.c
#include "ClassName.h"
static void method1(ClassName *this_c, char c);
static void method2(ClassName *this_c, char c);
...
static void methodN(ClassName *this_c, char c);
void initClassName(ClassName *this_c)
{
this_c->method1 = &method1;
this_c->method2 = &method2;
...
this_c->methodN = &methodN;
}
void method1(ClassName *this_c, char c)
{
//do something
}
void method2(ClassName *this_c, char c)
{
//do something
}
...
void methodN(ClassName *this_c, char c)
{
//do something
}
Everything works fine but, somewhere in the code, I define an array:
...
ClassName objects[200];
for(i = 0; i < 200; i++)
{
initClassName(&objects[i]);
}
...
Because of the function pointers, the memory usage associated to this array is quite high (I have several "methods" in this "class").
Considering that this is a code that will run in an embedded system, is there a better way to do it?
Defining functions outside the structure could be a possibility but in my opinion it does not respect what I'm trying to emulate.
I have to use only C, not C++.
What you have created is a very dynamic system where each object instance can have its own set of method implementations. That's why it uses so much memory.
Another approach is an implementation closer to early C++ (before multiple inheritance) where all instance of the same class share the same vtable. The vtable contains the function pointers.
typedef struct struct_ClassName ClassName;
typedef struct struct_ClassName_vtable ClassName_vtable;
struct ClassName_vtable
{
void (*method1)(ClassName*, char);
void (*method2)(ClassName*, char);
...
void (*methodN)(ClassName*, char);
}
struct ClassName
{
ClassName_vtable* _vtable;
char a;
char b;
char c;
};
void initClassName(ClassName*);
static void method1(ClassName *this_c, char c);
static void method2(ClassName *this_c, char c);
...
static void methodN(ClassName *this_c, char c);
ClassName_vtable _ClassName_vtable = {
method1,
method2,
...,
methodN
};
void initClassName(ClassName *this_c)
{
this_c->_vtable = _ClassName_vtable;
}
That way, the OO overhead per instance is only the size of a pointer. It's also easier to create subclasses.
A method call looks like this:
ClassName* obj = ...;
obj->vtable->method2(obj, 'a');
ClassName objects[200];
for(i = 0; i < 200; i++)
initClassName(&objects[i]);
I will show you a stripped-off version of something I use here
for similar effect. It is hard to say when a certain size is huge in terms of pointers or whatever. Each environment has their truth and this can be useless or useful...
Anyway, the ideia is encapsulation. And in C we have no this pointer. C is not C++ or java or javascript. Each class instance must have a table of function pointers for the methods. We need to build these tables. This is how virtual classes are implemented in others languages anyway. And if each class element can allocate memory code is needed to allocate and free memory.
TL;DR
Below is a C example of a program that builds and uses an array of classes. In this case an array of stacks. It shows a mechanism of building stacks of mixed things. Each array item has all that is needed to manage his own stack instance, be it of a trivial type or a complex structure. It can be easily changed to implement other tyoes of classes in C.
Please do not bother warning me that I cast the return of malloc(). I , as many others, do not like implicit things. And I know that C-FAQ is a never-updated thing from the end of the '90s so no need to reference this either.
An example: a static STACK container
typedef struct
{
int data[SIZE];
int limit; // capacity
int size; // actual
} Stack;
This is it: a simple stack of int values. Let us say we want to declare a vector of stacks of different things, but in C. And use methods on them. If we use trivial types --- in C++ we say the struct is trivialy constructible --- things can get easier, but if we are about to use structs we need to know about how to manipulate stack elements, since they can allocate memory.
We are writing a container so the methods of the class must work for any underlying data. And we have no iterators like C++ STL. Here we are implementing the POP TOP and PUSH methods for stacks, and a toString() method like in java to print values on the screen.
For each possible content in the container we need to have a constructor, a
destructor, a copy constructor and a display method. In this example we have just 2 types of stacks: a stack of int and a stack of struct Sample:
typedef struct
{
size_t _id;
char name[30];
char phone[20];
} Sample;
We can add others just by writing the required 4 functions.
main.c example
int main(void)
{
srand(220804); // for the factory functions
Stack* class_array[2] = {
create(4, create_i, copy_i, destroy_i, show_i),
create(3, create_st, copy_st, destroy_st, show_st)};
printf("\n\n=====> Testing with STACK of int\n\n");
class_test(class_array[0], factory_i);
printf(
"\n\n=====> Testing with STACK of struct "
"Sample\n\n");
class_test(class_array[1], factory_st);
class_array[0]->destroy(class_array[0]);
class_array[1]->destroy(class_array[1]);
return 0;
}
Each instance of Stack has pointers to the stack methods and to the functions that manipulate the stack data, so we can have a single class_test() function that does the following:
builds a stack of the required size, 4 or 3 in the example
fills the stack with data generated by factory functions (in production the logic builds the data)
shows the stack contents
removes all stack elements, one by one
At the end the destructor is called for eack stack.
The class.h file
typedef void* (PVFV)(void*);
typedef int (PIFV)(void*);
typedef struct
{
size_t size_;
size_t lim_;
void** data_;
PVFV* copy;
PVFV* destroy;
int (*show)(void*,const char*); // for testing
// constructor and destructor for container elements
PVFV* create_1;
PVFV* copy_1;
PVFV* destroy_1;
PIFV* show_1;
// member functions
PIFV* POP;
int (*PUSH)(void*,void*);
PVFV* TOP;
PIFV* empty;
size_t (*size)(void*);
} Stack;
Stack* create(
size_t,
void* (*)(void*),
void* (*)(void*),
void* (*)(void*),
int (*)(void*));
int class_test(Stack*, void* (*)());
the example output
=====> Testing with STACK of int
Stack is empty
POP() on empty stack returned -2
TOP() on empty stack returned NULL
Calls PUSH until error
Value inserted: 42
Value inserted: 41
Value inserted: 40
Value inserted: 39
Stack now has 4 elements
Stack has 4 of 4 elements:
42
41
40
39
Calls POP() until error
Stack size: 3
Stack size: 2
Stack size: 1
Stack size: 0
=====> Testing with STACK of struct Sample
Stack is empty
POP() on empty stack returned -2
TOP() on empty stack returned NULL
Calls PUSH until error
Value inserted: 0195 Sample id#0195 +76(203)6840-195
Value inserted: 0943 Sample id#0943 +35(686)9368-943
Value inserted: 0152 Sample id#0152 +16(051)8816-152
Stack now has 3 elements
Stack has 3 of 3 elements:
0096 Sample id#0096 +24(477)0418-096
0037 Sample id#0037 +27(214)3509-037
0836 Sample id#0836 +68(857)4634-836
Calls POP() until error
Stack size: 2
Stack size: 1
Stack size: 0
the logic
For each tye of element we need to write the 4 functions: they can alocate memory and be very complex or they can be trivial, but the class methods need to handle any case.
code for struct Sample in stack_struct.h###
#pragma once
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
void* copy_st(void*);
void* create_st(void*);
void* destroy_st(void*);
void* factory_st();
typedef struct
{
size_t _id;
char name[30];
char phone[20];
} Sample;
void* create_st(void* el)
{
return factory_st();
}
void* copy_st(void* el)
{
if (el == NULL) return NULL;
Sample* e = (Sample*)malloc(sizeof(Sample));
*e = *((Sample*)el);
return e;
}
void* destroy_st(void* el)
{
if (el == NULL) return NULL;
free(el);
return NULL;
}
int show_st(void* el)
{
if (el == NULL) return 0;
Sample* e = (Sample*)el;
printf(
" %04d %15s %20s\n",
(int) e->_id, e->name, e->phone);
return 0;
}
void* factory_st()
{
Sample* e = (Sample*)malloc(sizeof(Sample));
e->_id = rand() % 1000;
sprintf(e->name, "Sample id#%04d", (int)e->_id);
memset(e->phone, 0, sizeof(e->phone));
e->phone[0] = '+';
for (int i = 1; i <= 17; i += 1)
e->phone[i] = '0' + rand() % 10;
e->phone[3] = '(';
e->phone[7] = ')';
e->phone[12] = '-';
e->phone[13] = e->name[11];
e->phone[14] = e->name[12];
e->phone[15] = e->name[13];
e->phone[16] = e->name[14];
e->phone[17] = 0;
return (void*)e;
}
code for int elements stack_int.h_###
#pragma once
#include <stdio.h>
#include <stdlib.h>
void* create_i(void* el)
{
int* e = (int*)malloc(sizeof(int));
*e = *((int*)el);
return (void*)e;
}
void* copy_i(void* el)
{
if (el == NULL) return NULL;
int* e = (int*)malloc(sizeof(int));
*e = *( (int*)el );
return e;
}
void* destroy_i(void* el)
{
if (el == NULL) return NULL;
free(el);
return NULL;
}
int show_i(void* el)
{
if (el == NULL) return 0;
int v = *((int*)el);
printf(" %d\n", v);
return 0;
}
void* factory_i()
{
static int i = 42;
int* new_int = (int*)malloc(sizeof(int));
*new_int = i;
i -= 1;
return (void*)new_int;
}
The class implementation class.c
#include "class.h"
#include <stdio.h>
#include <stdlib.h>
void* Copy__(void*);
void* Destroy__(void*);
int POP__(void*);
int PUSH__(void*, void*);
int Show__(void*, const char*);
void* TOP__(void*);
int empty__(void*);
size_t size__(void*);
Stack* create(
size_t sz, void* (*create)(void*), void* (*copy)(void*),
void* (*destroy)(void*), int (*show)(void*))
{
Stack* stack = (Stack*)malloc(sizeof(Stack));
if (stack == NULL) return NULL;
stack->size_ = 0;
stack->lim_ = sz;
stack->data_ = (void*)malloc(sz * sizeof(void*));
stack->copy = Copy__;
stack->destroy = Destroy__;
stack->show = Show__;
stack->create_1 = create;
stack->copy_1 = copy;
stack->destroy_1 = destroy;
stack->show_1 = show;
stack->POP = POP__;
stack->PUSH = PUSH__;
stack->TOP = TOP__;
stack->empty = empty__;
stack->size = size__;
return stack;
}
void* Copy__(void* one) { return NULL; };
void* Destroy__(void* stack)
{ // before destructing a stack we need to
// destroy all elements
if (stack == NULL) return NULL;
Stack* st = (Stack*)stack;
for (size_t ix = 0; ix < st->size_; ix += 1)
(st->destroy_1)(st->data_[ix]);
free(st->data_);
free(st);
return NULL;
};
int POP__(void* stack)
{
if (stack == NULL) return -1; // no stack
Stack* st = stack;
if (st->size_ == 0) return -2; // empty
st->size_ -= 1; // one less
return 0; // ok
}
int PUSH__(void* el, void* stack)
{
if (el == NULL) return -1; // no element
if (stack == NULL) return -2; // no stack
Stack* st = (Stack*)stack;
if (st->size_ == st->lim_) return -3; // full
void* new_el = st->create_1(el); // copy construct
st->data_[st->size_] = new_el;
st->size_ += 1; // one up
return 0; // ok
}
int Show__(void* stack, const char* title)
{
if (stack == NULL) return -1;
Stack* st = stack;
if (title != NULL) printf("%s\n", title);
if (st->size_ == 0)
{
printf("Stack is empty\n");
return 0;
}
for (size_t ix = 0; ix < st->size_; ix += 1)
st->show_1(st->data_[ix]);
printf("\n");
return 0;
}
void* TOP__(void* stack)
{
if (stack == NULL) return NULL; // no stack
Stack* st = stack;
if (st->size_ == 0) return NULL; // empty
return st->data_[st->size_ - 1]; // ok
}
int empty__(void* stack)
{
if (stack == NULL) return 1; // empty??
return ((Stack*)stack)->size_ == 0;
}
size_t size__(void* stack)
{
if (stack == NULL) return 1; // empty??
return ((Stack*)stack)->size_;
}
///////////// TEST FUNCTION ///////////////
int class_test(Stack* tst, void* (*factory)())
{
if (tst == NULL) return -1;
// is stack empty?
if (tst->empty(tst))
printf("Stack is empty\n");
else
printf("Stack: %zd elements\n", tst->size(tst));
int res = tst->POP(tst);
printf("POP() on empty stack returned %d\n", res);
void* top = tst->TOP(tst);
if (top == NULL)
printf("TOP() on empty stack returned NULL\n");
else
{
printf(
"\nTOP() on empty stack returned NOT NULL!\n");
return -2;
}
printf("Calls PUSH until error\n\n");
void* one = factory();
int value = *(int*)one;
while (tst->PUSH(one, tst) == 0)
{
printf("Value inserted:");
tst->show_1(one);
free(one);
one = factory();
}
free(one); // last one, not inserted
printf("Stack now has %zd elements\n", tst->size(tst));
char title[80] = {" "};
sprintf(
title, "\nStack has %zd of %zd elements:\n",
tst->size_, tst->lim_);
tst->show(tst, title);
// agora esvazia a pilha ate dar erro
printf("\nCalls POP() until error\n");
while (tst->POP(tst) == 0)
printf("Stack size: %I32d\n", (int)tst->size(tst));
return 0;
};
The complete main.c program
#include <stdio.h>
#include "class.h"
#include "stack_int.h"
#include "stack_struct.h"
int main(void)
{
srand(220804);
Stack* class_array[2] = {
create(4, create_i, copy_i, destroy_i, show_i),
create(3, create_st, copy_st, destroy_st, show_st)};
printf("\n\n=====> Testing with STACK of int\n\n");
class_test(class_array[0], factory_i);
printf(
"\n\n=====> Testing with STACK of struct "
"Sample\n\n");
class_test(class_array[1], factory_st);
class_array[0]->destroy(class_array[0]);
class_array[1]->destroy(class_array[1]);
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef void SeqList;
typedef void SeqListNode;
typedef unsigned int TSeqListNode;
typedef unsigned int TSeqListNode;
typedef struct _tag_SeqList
{
int capacity;
int length;
TSeqListNode *node;
}TSeqList;
SeqList * SeqList_Create(int capacity)
{
TSeqList *ret = NULL;
if(capacity>=0)
{
ret = (TSeqList *)malloc(sizeof(TSeqList) + sizeof(TSeqListNode)*capacity);
}
if(ret == NULL)
{
printf("malloc fail.\n");
exit(-1);
}
ret->capacity = capacity;
ret->length = 0;
ret->node = (TSeqListNode*)(ret+1);
return ret;
}
int SeqList_Insert(SeqList *list, SeqListNode *node, int pos)
{
TSeqList *sList = (TSeqList*)list;
int ret = (sList != NULL);
int i = 0;
ret = ret && (sList->length+1 <= sList->capacity);
ret = ret && (0 <= pos);
if(ret)
{
if(pos >= sList->length)
{
pos = sList->length;
}
for(i=sList->length; i > pos; i--)
{
sList->node[i] = sList->node[i-1];
}
sList->node[i] = (TSeqListNode)node;
sList->length++;
}
return ret;
}
int main()
{
system("pause");
return 0;
}
D:\mingw64\bin\gcc.exe -g D:\Cpp\DSA\test001.c -o D:\Cpp\test001.exe
D:\Cpp\test001.c: In function 'SeqList_Insert':
D:\Cpp\test001.c:56:26: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
sList->node[i] = (TSeqListNode)node;
^
What you are trying to do is already part of the ISO/IEC 9899:1999 C standard. It's called flexible array member. So no need for inventing your own code - just use what is already available.
Also notice that capacity and length are variables that can't be negative. So you should use an unsigned type. The common used type would be size_t.
So your code should be:
typedef struct _tag_SeqList
{
size_t capacity;
size_t length;
TSeqListNode node[]; // Flexible array member
} TSeqList;
Now you can simply do:
ret = malloc(sizeof(TSeqList) + sizeof(TSeqListNode)*capacity);
\--------------/ \----------------------------/
Memory for the Memory for the 'node' array
struct, i.e for with 'capacity' elements
capacity and
length
to allocate (no need for checking the value of capacity as it can't be negative).
And just use node as a normal array.
All the need for casts are gone.
Earlier versions of the C standard (C90) probably do necessitate casting. I think a placeholder data (int *)(array + 1) is entirely appropriate in some situations, assuming alignment is respected. Note that you might also make this design decision to comply with MISRA C 2012 Rule 18.7. I have eliminated the obfuscating typedefs and renamed parts of it to reflect what it actually does more clearly. A cast to void * is basically turning off type-checking, (through a typedef or not,) and should be avoided where possible.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct IntArray {
size_t capacity;
size_t length;
int *node;
};
/** #throws[malloc] */
static struct IntArray *IntArray_Create(const size_t capacity)
{
struct IntArray *ret = NULL;
ret = malloc(sizeof *ret + sizeof *ret->node * capacity);
if(ret == NULL) return 0; /* Let the caller decide what to do. */
ret->capacity = capacity;
ret->length = 0;
ret->node = (int *)(ret+1);
return ret;
}
/** Returns false if the `array` is full or null. Clips `pos` to `array`. */
static int IntArray_Insert(struct IntArray *array, const int node, size_t pos)
{
if(!array || array->length >= array->capacity) return 0;
if(pos >= array->length) pos = array->length;
/* Replaced looping over the array for performance. */
memmove(array->node + pos + 1, array->node + pos, array->length - pos);
array->length++;
array->node[pos] = node;
return 1;
}
#include <assert.h>
int main(void)
{
struct IntArray *a;
if(!(a = IntArray_Create(3))) { perror("fail"); return EXIT_FAILURE; };
assert(IntArray_Insert(a, 1, 42));
assert(IntArray_Insert(a, 3, 1));
assert(IntArray_Insert(a, 2, 1));
assert(!IntArray_Insert(a, 2, 1));
free(a);
return 0;
}
If you are targeting C99 or later, the flexible array member may be useful for this exact reason. If this is part of a macro, the only typedef you really need is typedef int ArrayType;.
I my code i call the insert function and it passes a pointer to the struct (table) and the insert function recieves a pointer and does some stuff and returns it again. But running the code gives segmentation fault. when i try to access the values in the struct array using the pointer passed.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define __USE_BSD
#include <string.h>
#include "speller.h"
#include "dict.h"
typedef struct
{ // hash-table entry
Key_Type element; // only data is the key itself
enum {empty, in_use, deleted} state;
} cell;
typedef unsigned int Table_size; // type for size-of or index-into hash table
struct table
{
cell *cells; Table_size table_size; // cell cells [table_size];
Table_size num_entries; // number of cells in_use
// add anything else that you need
};
int hashfunc(Key_Type k, Table_size size)
{
printf("enterd\n");
char * d = k;
int hash = 0;
int c;
printf("%s\n", d);
printf("wtf??\n");
while (c = *d++)
{
printf("maybehere??\n");
hash = hash + c;
}
hash = hash%size;
printf("%d\n", hash);
return hash;
}
Table initialize_table (Table_size size)
{
Table t = malloc(sizeof(struct table));
t->table_size = size;
cell hash_table[size];
for (int i=0; i<size; i++)
{
hash_table[i].state = empty;
hash_table[i].element = "-";
//printf("initialised\n");
}
t->num_entries = 0;
t->cells = hash_table;
/*for (int i = 0; i < t->table_size; i++)
{
printf("%d %s\n", i, (t->cells + i)->element);
}*/
return t;
}
int a = 0;
Table insert (Key_Type k, Table t)
{
//printf("insert called %d\n", a);
printf("%d\n", t->table_size);
//printf("%s\n", (t->cells + 2)->element);
// as soon as program reaches here i get output like - 1 (NULL)
2 (NULL) and then segmentation fault
for (int i = 0; i < t->table_size; i++)
{
printf("%d %s\n", i, (t->cells + i)->element);
}
a++;
printf("%s\n", k);
int hash_code = hashfunc(k, t->table_size);
// Linear Probing
printf("im here\n");
while(strcmp((t->cells + hash_code)->element,"-") != 0)
{
if (strcmp((t->cells + hash_code)->element,k) == 0)
{
printf("return at if\n");
return t;
}
else if (hash_code == (t->table_size - 1))
hash_code = 0;
else
hash_code++;
}
(t->cells + hash_code)->element = k;
(t->cells + hash_code)->state = in_use;
t->num_entries += 1;
printf("return at end with value %s\n", k);
printf("inserted value %s\n", (t->cells + hash_code)->element);
return t;
}
Boolean find (Key_Type k, Table t)
{
return FALSE;
}
void print_table (Table t)
{
Table_size size = t->table_size;
for (int i = 0; i<size; i++)
{
if (strcmp((t->cells + i)->element,"-") != 0)
printf("%d %s\n", i, (t->cells + i)->element);
}
}
void print_stats (Table t)
{
}
void main()
{
Table table;
Table_size table_size = 19;
int a = 5;
Key_Type input[5] = {"a","b","ab","abc","abcd"};
table= initialize_table (table_size);
//printf("%s\n", input[1]);
while (a)
{
table= insert("a",table);
a--;
}
printf("printing table\n");
print_table(table);
}
this is the dict.h code
typedef char* Key_Type;
typedef struct table* Table; // allows different definitions of struct table
Table initialize_table (); // allows different parameters
Table insert (Key_Type, Table);
Boolean find (Key_Type, Table);
void print_table (Table);
void print_stats (Table);
This is the speller.h code
typedef enum {FALSE, TRUE} Boolean;
extern int verbose; // used to control monitoring output
extern int mode; // used to control your algorithm
extern char *prog_name; // used by check
void check (void *memory) ; // check result from strdup, malloc etc.
I believe i dont understand how the pointers work in this program.
Here is the problem,
cell hash_table[size];
and then, you make t->cells point to hash_table but hash_table is a local variable in the initialize_table() function, so it's destroyed/deallocated when the function returns and no longer accessible after it returns.
You should allocate it on the heap too, like this
cell *hash_table;
hash_table = malloc(size * sizeof(*hash_table));
if (hash_table == NULL)
return NULL; // Probably free `t' so that no memory leaks
// happen
Accessing such a local variable that was allocated in the stack frame of a function, after that function returns is undefined behavior, the problem could happen somewhere else in the code or when accessing the pointer pointing to the deallocated data.
A side note
Be consistent with naming, and unambigous, you used a weird CamelCase and underscore combination, it doesn't matter if it's weird or not, keep it and preserve it throughout the code — respect your own style. And call the cell typedef: Cell instead.
Also, always check the return value of malloc() which returns NULL on error (allocation failure), you should write code as if all the bad things will happen, because they do.
And finally, never typedef a pointer. It doesn't help whatsoever, it only obscures the fact that a declaration is that of a pointer.
For example, if I defined a Stack ADT in C, normally my type definition -using an array based implementation- is like this:
typedef char StackEntry;
typedef struct stack {
int top;
StackEntry entry[MAXSTACK];
} Stack;
How can I make my Stack such that I can have one Stack for characters and another Stack -in the same program- that handles integers for example ?
You could use a stack-structure that is initialized with the size of the elements and that reserves a memory block in terms of bytes to store the elements:
typedef struct stack {
int top;
size_t size;
size_t maxElems;
char content[];
} Stack;
Stack *createStack(size_t size, size_t maxElems) {
Stack *result = malloc(sizeof(Stack)+size*maxElems);
result->top=0;
result->size=size;
result->maxElems=maxElems;
return result;
}
int push(Stack *stack, void *elem) {
if (stack->top == stack->maxElems)
return -1;
memcpy(stack->content + stack->top * stack->size, elem, stack->size);
stack->top++;
return stack->top;
}
int pop(Stack *stack, void *elem) {
if (stack->top == 0)
return -1;
stack->top--;
memcpy(elem, stack->content + stack->top * stack->size, stack->size);
return stack->top;
}
int main() {
Stack *charStack = createStack(sizeof(char), 10);
for (int i=0; i<10; i++) {
char c = 'A'+i;
push(charStack, &c);
}
char c;
while (pop(charStack, &c) >= 0) {
printf("%c\n", c);
}
free(charStack);
Stack *intStack = createStack(sizeof(int), 10);
for (int i=0; i<10; i++) {
push(intStack, &i);
}
int i;
while (pop(intStack, &i) >= 0) {
printf("%d\n", i);
}
free(intStack);
}
Use macros, e.g.
#define STACK_TYPE(type) struct { int top; type entry[MAXSTACK]; }
You may well end up using macros for the stack operations such as push/pop too:
#define STACK_PUSH(stack, value) \
do { if ( (top) < MAXSTACK ) (stack).entry[(stack).top++] = (value); } while (0)
In most cases you wouldn't need to pass the stack type as a macro parameter, since the same syntax works for all stacks.
Sample usage:
STACK_TYPE(int) mystack = { 0 };
STACK_PUSH(mystack, 5);
Obviously there are a lot of different ways you could do the details.
I am trying to implement a generic array list in C. However, when the data type is any type other than int, the list wont contain the correct data. For example, like 123.1234 as a double, when the double is passed into the list, it will become 000.0000. One when the data type is int, it will have correct value. I don't know which part of the code is wrong, can anyone give me a hint? Thanks
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "genericADT.h"
struct list_type {
void *data;
int elementSize;
int size;
int capacity;
};
ListType create(int elementSize) {
ListType listptr = malloc(sizeof(struct list_type));
if (listptr != NULL) {
listptr->size = 0;
listptr->capacity = 10;
listptr->elementSize = elementSize;
listptr->data = malloc(10 * (listptr->elementSize));
if (listptr->data == NULL) {
free(listptr);
listptr = NULL;
}
}
return listptr;
}
void push(ListType listptr, void *item) {
if (listptr->size >= listptr->capacity) {
void *temp = realloc(listptr->data, listptr->elementSize * (listptr->capacity + 100));
if (temp != NULL) {
listptr->capacity += 100;
listptr->data = temp;
memcpy(listptr->data + (listptr->size) * (listptr->elementSize), item, sizeof(listptr->elementSize));
listptr->size++;
}
} else {
memcpy(listptr->data + (listptr->size) * (listptr->elementSize), item, sizeof(listptr->elementSize));
listptr->size++;
}
}
void *get(ListType listptr, int index) {
return listptr->data + index * (listptr->elementSize);
}
int size_is(ListType listptr) {
return listptr->size;
}
There are minor problems in your code, but it correctly processes double values.
First as noticed by #n.m., you really want to use listptr->elementSize instead of sizeof(listptr->elementSize)
Next, as you want to do pointer arithmetic, you should declare data as char * and not void *
Last as a elementary optimization, you pull the actual insertion code after the test for capacity instead of duplicating it in both branches.
But after those fixes, this main correctly stores and extract double:
int main() {
ListType ls = create(sizeof(double));
double f1=1.5, f2=3.6;
push(ls, &f1);
push(ls, &f2);
printf("Got %f %f\n", *((double *) get(ls, 0)), *((double *) get(ls, 1)));
return 0;
}
it prints as expected:
Got 1.500000 3.600000