paul_schaefer
paul_schaefer

Reputation: 431

C Union definition

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

Answers (2)

Sergey Kalinichenko
Sergey Kalinichenko

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 sizeofs, 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 chars, and convert void* pointer to int*, float*, etc. This would lead to a different syntax.

Upvotes: 2

QuestionC
QuestionC

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

Related Questions