【问题标题】:C Union definitionC 联合定义
【发布时间】:2016-03-27 18:04:18
【问题描述】:

在一个结构中,我需要一些空间来放置一些东西。这个空间必须能够收集所有数据类型,所以我想定义一个联合。空间限制为 n 个字节(无符号字符)。

我必须如何定义我的联合,以便它可以包含 char、int、float 等?

我必须这样做吗?

#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;

或者是否有可能只定义 unsigned char 数组的大小,然后自动定义 int 数组的大小?如果SIZE 不能被 4 整除,会发生什么?

编辑:(与 cmets 有关)

我想构建一个类似定时事件处理程序的东西。这意味着,我有一个包含事件数组的结构。每个事件都有一个执行时间和一个相关函数(存储为指针)。当事件处理程序的定时器计数器与事件执行时间匹配时,我调用相关函数。在我会知道的函数中,需要哪些参数,所以我不需要保存标签值。问题是,事件是在函数中创建的,并且因为我不想使事件静态(以节省内存),所以我向事件处理程序添加了一些内存(环形缓冲区),所有函数都可以放入一些数据.每个事件都有一个变量,其中包含指向(第一个)数据的指针。数据类型只有nativ数据类型,没有自己的structs。

这是我当前的代码:

startSystemClock() 将在启动时调用

executeSystemEvent() 将被定时器 1 的中断服务程序通过设置sysEventHandler.execute=TRUE 调用,while(1) 循环检查这个标志然后调用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;
}

要添加事件,我会在任何函数中编写:

systemEvent_t event;
event.fct.voidFct_data=&enablePinChangeInterrupt_wrapper;
event.data.i=storeSystemEventData((int)PUSHBUTTON_CN_BIT);
event.time=10;
addSystemEvent(event);

我知道,storeSystemEventData-函数不完整。但对于我的第一个目的,我只需要 int,所以它可以工作。

【问题讨论】:

  • 当您说所有数据类型时,您是指纯旧数据类型,例如int 等,还是您还需要用户定义的数据类型,例如struct?您是否需要考虑其他更具体的数据类型,例如 32 位或 64 位有符号/无符号整数?您真的需要char 和unsigned char 以外的类型数组,因为这些数组通常用于文本字符串,或者该内存空间用于包含文本字符串或某种二进制数?
  • 另一个问题是您如何知道存储在联合中的实际数据类型?通常情况下,在包含标记值或枚举的结构中使用联合,该标记值或枚举指示存储在联合中的实际数据类型。
  • 在我看来,你原来的工会会很好。可能希望使用挂钩到计时器的函数,该函数处理待处理事件列表,减少计时器计数,当为零时,执行事件函数。事件函数然后决定它是必须重置以再次倒计时的重复的周期性事件,还是必须在执行后从计时器事件列表中删除的仅一次。我会谨慎禁用中断。这是一个多线程应用程序还是一个处理多个事件的单线程?您对同步和计时器有哪些操作系统支持?
  • 程序在 PIC32MX795F512L 微控制器上运行。没有操作系统,没有线程等等......计时器在我计算时间的中断内引发了一个中断。由于执行中断所需的时间必须很小,我不想倒计时每个事件的时间。我将一个事件放入循环缓冲区。当事件需要周期性执行时,从事件中调用的函数会再次将事件放入队列中。
  • 但我认为,我将使用 int[..] 定义联合的大小,所有其他的 ..[0]并且总是计算int-pointer,即使我输入了一个字符。当然,这不是最有效的方法,因为在放入 char 的情况下,我将“浪费”3 个字节。但一个优点是,数据与可分割的地址对齐......

标签: c


【解决方案1】:

如果 SIZE 不能被 4 整除,会发生什么?

我假设您问的是关于被 4 整除的问题(而不是任何其他数字),因为它是一个常见的 sizeof(int)。当SIZE 不能被任何sizeofs 整除时,最终会得到完全适合size 的最大数组,即该数字将被截断。例如,当 sizeof(int) 为 4 时,将 SIZE 设置为 13 会产生

int sint[3];

换句话说,大小将被“四舍五入”(截断)。如果您更喜欢四舍五入,请使用以下表达式:

unsigned int uint[(SIZE+sizeof(unsigned int)-1)/sizeof(unsigned int)];

但是请注意,uint[] 数组的大小可能会超过 uchar 的大小。

是否有可能只定义 unsigned char 数组的大小,然后自动定义 int 数组的大小?

您可以将union 替换为chars 的数组,并将void* 指针转换为int*、float* 等。这将导致不同的语法。

【讨论】:

  • @nouney 在使用别名数组的情况下,void* 更好,因为它为您的代码的读者提供了更多的清晰度。当我需要手动对内存块进行分区时,我通常更喜欢char* 而不是void*,因为char* 让我可以在不进行强制转换的情况下进行指针运算。
  • 您可以将 union 替换为字符数组,并将 void 指针转换为 int*、float* 等* 我注意到这样做可能会导致失败例如针对不同数据类型具有不同对齐限制的架构上的 SIGBUS。例如,在联合中有一个显式的 double 数组可以避免这种情况。此外,正如您所提到的,void * 上的指针运算是非标准的 - 我怀疑您的评论会产生一些“但我一直在 void * 上进行指针运算!”想法,如果不是实际的 cmets...
【解决方案2】:

您不需要指定数组大小,最大的除外。只是越界访问其他类型。

#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 不提供数组边界检查两种方式,所以如果你尝试访问内存对象之外的内存,你会很不走运。

【讨论】:

  • 是将数组大小设置为零的常用方法还是更常用的“hack”?
  • 我不知道。没有常见的 hack,因为使用这样的联合编写代码非常罕见。
  • 什么是做我想做的事情的合适方法?
  • 如果数组大小信息和索引值在执行越界访问时可用,一些 C 编译器以及静态分析工具将针对越界访问提供警告。还有一些带有工具的 C IDE,当在调试模式下编译和在调试模式下使用时,它们会执行数组越界检查。
  • 我需要对您的问题进行更高级别的描述以建议替代方法。 “收集所有数据类型”太模糊了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-12
  • 2018-11-08
  • 1970-01-01
  • 2019-04-15
  • 2020-05-30
  • 1970-01-01
相关资源
最近更新 更多