Reputation: 431
Within a struct I need some space where I can put in something. This space have to be able to collect all data types, so I want to define an union. The space is limited to n bytes (unsigned char).
How do I have to define my union, so that it can contain char, int, float and so on?
Have I to do it this way?
#define SIZE (128)
union {
unsigned char uchar[SIZE];
char schar[SIZE];
unsigned int uint[SIZE/sizeof(unsigned int)];
int sint[SIZE/sizeof(int)];
float flt[SIZE/sizeof(float)];
double dbl[SIZE/sizeof(double)];
}memory;
Or is there a possibility to define only the size of the unsigned char array and then to define size of the int array automatically? What does happen, if SIZE
isn't divisible by 4?
EDIT: (related to the comments)
I want to build something like an timed event handler. That means, I have a struct containing an array of events. Each event has an execution time and a related function (stored as a pointer). When the timer counter of the event handler matches the event execution time, I call the related function. Within the function I will know, wich arguments are expected, so I don't need to save a tag value. The problem is, that the events are created within a funtion and because I don't want to make the events static (to save memory), I added some memory (ring buffer) to my event handler where all functions can put in some data. Each event will have a variable containing the pointer to the (first) data. The type of data are only the nativ data types, no own structs.
This is my current code:
startSystemClock()
will be called at start up
executeSystemEvent()
will be called by the interrupt service routine of timer 1 by setting sysEventHandler.execute=TRUE
and a while(1)
-loop checks this flag and then calls executeSystemEvent()
// typedefs requird for timed events
typedef union __attribute__ ((packed)){
int *i; // pointer, where data is stored
int value; // if there is a pointer assigned, value differs from zero
}systemEventData_u;
typedef union __attribute__ ((packed)){
int value; // if there is a pointer assigned, value differs from zero
void (*voidFct_noData)();
void (*voidFct_data)(systemEventData_u);
}systemEventFct_u;
typedef struct{
int time;
unsigned int id;
systemEventFct_u fct;
systemEventData_u data;
}systemEvent_t;
#define SYSTEM_EVENT_HANDLER_BUFFER_SIZE (10)
#define SYSTEM_EVENT_HANDLER_MEMORY_SIZE (10)
typedef struct{
unsigned int actualCnt;
unsigned int nextEventCnt;
unsigned char execute;
systemEvent_t events[SYSTEM_EVENT_HANDLER_BUFFER_SIZE];
systemEvent_t* write;
// create some persistent memory usable by all functions
int* memWrite;
union __attribute__ ((packed)){
unsigned char uchar[0];
char schar[0];
unsigned int uint[0];
int sint[SYSTEM_EVENT_HANDLER_MEMORY_SIZE];
float flt[0];
double dbl[0];
}memory;
}systemEventHandler_t;
void startSystemClock(){
// initialize event handler
sysEventHandler.actualCnt=0;
sysEventHandler.nextEventCnt=-1;
sysEventHandler.execute=FALSE;
sysEventHandler.write=sysEventHandler.events;
sysEventHandler.memWrite=sysEventHandler.memory.sint;
unsigned int i=SYSTEM_EVENT_HANDLER_BUFFER_SIZE;
systemEvent_t *ptr=sysEventHandler.events;
while(i--){
ptr->fct.value=0;
ptr->data.value=0;
ptr->time=0;
ptr++;
}
// initialize timer 1
TMR1 = 0x00;
T1CON = T3_OFF | T3_IDLE_CON | T3_GATE_OFF | T1_PS_1_8 | T1_SOURCE_INT;
IPC1SET = (INTERRUPT_PRIOR_TIMER1 << _IPC1_T1IP_POSITION) | (INTERRUPT_SUB_PRIOR_TIMER1 << _IPC1_T1IS_POSITION);
IFS0CLR = (1 << _IFS0_T1IF_POSITION);
IEC0SET = (1 << _IEC0_T1IE_POSITION);
PR1 = PR_TIMER1;
T1CONSET = (1 << _T1CON_ON_POSITION);
print_text("timer1 started\n\r");
}
void executeSystemEvent(){
asm("di");
int time=sysEventHandler.actualCnt;
asm("ei");
unsigned int i=SYSTEM_EVENT_HANDLER_BUFFER_SIZE;
unsigned int nextEventCnt=-1;
systemEvent_t *ptr=sysEventHandler.events;
while(i--){
// do not investigate, if there is no function pointer
// no function pointer means no event action
if(ptr->fct.value){
if(time>=ptr->time){
// execute function
if(ptr->data.value){
(*ptr->fct.voidFct_data)(ptr->data);
}else{
(*ptr->fct.voidFct_noData)();
}
ptr->fct.value=0;
}
}
ptr++;
}
// determine next event
// iterate again through whole queue to take added events into account also
i=SYSTEM_EVENT_HANDLER_BUFFER_SIZE;
ptr=sysEventHandler.events;
while(i--){
if(ptr->fct.value){
// get execution time to determine next one
if(ptr->time<nextEventCnt){
nextEventCnt=ptr->time;
}
}
ptr++;
}
asm("di");
sysEventHandler.nextEventCnt=nextEventCnt;
sysEventHandler.execute=FALSE;
asm("ei");
}
void addSystemEvent(systemEvent_t event){
// check, if this event will be the first event to execute
asm("di");
// get event execution time
event.time+=sysEventHandler.actualCnt;
// check, if it will be the next one to execute
if(sysEventHandler.nextEventCnt>event.time){
sysEventHandler.nextEventCnt=event.time;
}
asm("ei");
*sysEventHandler.write=event;
if(++sysEventHandler.write>=sysEventHandler.events+SYSTEM_EVENT_HANDLER_BUFFER_SIZE){
sysEventHandler.write=sysEventHandler.events;
}
}
int * storeSystemEventData(int data){
int *ptr=sysEventHandler.memWrite;
*ptr=data;
if(++sysEventHandler.memWrite>=sysEventHandler.memory.sint+SYSTEM_EVENT_HANDLER_MEMORY_SIZE){
sysEventHandler.memWrite=sysEventHandler.memory.sint;
}
return ptr;
}
To add an event, I write within any function:
systemEvent_t event;
event.fct.voidFct_data=&enablePinChangeInterrupt_wrapper;
event.data.i=storeSystemEventData((int)PUSHBUTTON_CN_BIT);
event.time=10;
addSystemEvent(event);
I know, that the storeSystemEventData
-function isn't complete. But for my first purpose, I only need int, so it works.
Upvotes: 0
Views: 182
Reputation: 726539
What does happen, if SIZE isn't divisible by 4?
I assume you ask the question about divisibility by 4 (as opposed by any other number) because it is a common sizeof(int)
. When SIZE
is indivisible by any of the sizeof
s, would end up with the largest array that fits fully inside the size
, i.e. the number would be truncated. For example, setting SIZE
to 13
when sizeof(int)
is 4 would produce
int sint[3];
In other words, the size would be "rounded down" (truncated). If you prefer rounding up, use this expression:
unsigned int uint[(SIZE+sizeof(unsigned int)-1)/sizeof(unsigned int)];
Note, however, that the size of uint[]
array may exceed the size of uchar
.
is there a possibility to define only the size of the unsigned char array and then to define size of the int array automatically?
You could replace union
with an array of char
s, and convert void*
pointer to int*
, float*
, etc. This would lead to a different syntax.
Upvotes: 2
Reputation: 10064
You don't need to specify the array sizes except for the biggest. Just out-of-bounds access the other types.
#include "stdio.h"
union memory {
unsigned char uchar[128];
char schar[0];
unsigned int uint[0];
int sint[0];
float flt[0];
double dbl[0];
} ;
int main (void)
{
union memory my_mem;
my_mem.schar[5] = 'A';
my_mem.schar[6] = 'B';
my_mem.schar[7] = 'C';
my_mem.schar[8] = 'D';
printf ("%d\n", my_mem.uint[1]);
return 0;
}
C doesn't provide array bounds checking either way, so you're just out of luck if you try to access memory outside the memory object.
Upvotes: 3