搜索
bottom↓
回复: 92

结构体与联合体

  [复制链接]

出0入0汤圆

发表于 2014-9-29 17:00:50 | 显示全部楼层 |阅读模式
最近接手了别人做的一个项目,问题不少。
在阅读程序的过程中看到其变量定义上的做法很是让我费解,也让我很郁闷。
他的做法如下,是在联合体里嵌套了多个结构体:

typedef  union  _RUN_PARAM{
        unsigned char         array[21];       
        struct
        {
                unsigned char         name;                                // 0                       
                unsigned char         run_status;                        // 1
                unsigned char         wash_pos;
                unsigned char         solvent_num;
                unsigned char         spe_num;                        //4// 4
                unsigned long                 count;                                // 5
                unsigned int                 atuo_wash;
                unsigned int                 speed_last;
                unsigned int                 second;
                unsigned int                 speed;
                unsigned long                 recycle;       
        }path_sol;
        struct
        {
                unsigned char         name;                                // 0                       
                unsigned char         run_status;                        // 1
                unsigned char         wash_pos;
                unsigned char         back_num;
                unsigned char         spe_num;                        //4// 4
                unsigned long                 count;                                // 5
                unsigned int                 speed_push;
                unsigned int                 count_push;
                unsigned int                 second_push;
                unsigned int                 speed;
                unsigned long                 recycle;       
        }path_sam;
        struct
        {
                unsigned char         name;                                // 0
                unsigned char         run_status;                        // 1
                unsigned int                 temperature;
                unsigned char                spe_num;                        //4// 4
                unsigned long                 count;                                // 5
                unsigned long                 times;
                unsigned int                 second;
                unsigned int                 speed;
                unsigned long                 recycle;
        }path_other;
}run_param;

而据我用过的新唐单片机里的写法刚好跟他相反,是结构体里嵌套联合体。
众所周知,union模型是共用一段内存,如果是里面再嵌套了struct,那在后续变量操作时难免会互相修改,从而增加产生BUG的机率。
我不知道这个人这样做的目的何在,可能是为了省内存(因为公司想要马儿跑又要马儿不吃草,用资源很少的单片机来做太多的事),但是会想到省内存的人
不会不考虑到互相更改的可能。也许这样的写法有其巨大的好处但我确实看不出来,因为我也是遇到问题才去研究。
当然若有论坛牛人经过也请放缓脚步,为我这种新手普及一下知识。
此只是发发唠骚,不喜勿喷。

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

 楼主| 发表于 2014-11-5 22:30:56 | 显示全部楼层
本帖最后由 blavy 于 2014-11-5 22:37 编辑

最近学习到一个联合体的好用的方法,可以把几个不在同一个PORT口的引脚结合起来,比如PA1,PB1,PC1,PD1,可以把这几个脚放到一个字节里面用,详细如下:
typedef union         
{
        uint8_t byte;
        struct
        {
                uint8_t B0:1;
                uint8_t B1:1;
                uint8_t B2:1;
                uint8_t B3:1;
                uint8_t B4:1;
                uint8_t B5:1;
                uint8_t B6:1;
                uint8_t B7:1;
        }BITS;
}BYTE_T;
先定义一个联合体分位定义的,这样里面的结构体就和byte共享同一段内存。
void DATA_RESOLVE (uint8_t DATAS)
{
        BYTE_T BUFF;
        BUFF.byte = DATAS;
        PA1 = BUFF.BITS.B0;
        PB1 = BUFF.BITS.B1;
        PC1 = BUFF.BITS.B2;
        PD1 = BUFF.BITS.B3;
//        =BUFF.BITS.B4;
//        =BUFF.BITS.B5;
//        =BUFF.BITS.B6;
//        =BUFF.BITS.B7;
}
这样一来,如果你想要四个脚输出PA1=1,PB1=0,PC1=0,PD2=0;只要用一条调用语句 DATA_RESOLVE (0x01);即可,可以把分散的引脚整合到一起,象一个PORT口里的一样。

出0入0汤圆

发表于 2014-12-2 17:10:28 | 显示全部楼层
对于位的声明,要注意对齐。
之前这样定义
union mSol
{
struct
{
  unsigned mSOL_Pos_L:    12;
  unsigned mSOL_Feh_L:      2;
  unsigned mSOL_Ref_L:       2;
  unsigned reserved:             8;
  unsigned mSOL_Pos_R:   12;
  unsigned mSOL_Feh_R:      2;
  unsigned mSOL_Ref_R:       2;
}name;
unsigned char byte[6];
};
出现数据丢失。
才发现是对齐问题,改为下面的就好啦。惭愧惭愧。
union mSol
{
struct
{
  unsigned mSOL_Pos_L_L:    8;
  unsigned mSOL_Pos_L_H:   4;
  unsigned mSOL_Feh_L:      2;
  unsigned mSOL_Ref_L:       2;

  unsigned reserved:             8;  
unsigned mSOL_Pos_R_L:   8;

  unsigned mSOL_Pos_R_H:  4;
  unsigned mSOL_Feh_R:      2;
  unsigned mSOL_Ref_R:       2;
  unsigned reserved:             8;
}name;
unsigned char byte[6];
};

出0入0汤圆

发表于 2014-9-29 17:17:59 | 显示全部楼层
省内存,可共享使用也可交替使用

出0入0汤圆

发表于 2014-9-29 17:21:14 | 显示全部楼层
这种用法很常见啊。
unsigned char   array[21]定义了一个char类型的数组,长度是21,后面的几个结构体都是跟这个数组共享同一个数据空间。
或者这么说,这段程序定义了一段内存空间,使用union结构体可以有多种方式去访问这段内存。你可以使用数组方式去访问,比如要对这段内存初始化清零的时候,使用数组就方便多了。同样的,如果你只需要访问内存中的某个字段,就用结构体的方式。定义多个结构体,可能是因为不同的对象,其属性不完全相同。
不好意思,说的不是很清楚。
单片机编程,本来就是要考虑节省内存,在哪里都一样,嵌入式编程就是这样,不比计算机编程,想怎么样就怎么样。

出0入0汤圆

发表于 2014-9-29 17:22:43 | 显示全部楼层
carolxr 发表于 2014-9-29 17:21
这种用法很常见啊。
unsigned char   array[21]定义了一个char类型的数组,长度是21,后面的几个结构体都是 ...

正解,这样写,对内存操作很灵活

出0入0汤圆

发表于 2014-9-29 17:34:27 | 显示全部楼层
他这个联合体定义了4个东西(一个数组+三个结构体),这样只是为了方便存取,并不是想拿这一份内存保持4份内容,实际也是不可能的。

出0入4汤圆

发表于 2014-9-29 18:04:13 | 显示全部楼层
学习了。。

出0入0汤圆

发表于 2014-9-29 18:10:37 | 显示全部楼层
围观………………

出0入0汤圆

发表于 2014-9-29 18:14:20 | 显示全部楼层
围观………………

出0入0汤圆

发表于 2014-9-29 19:33:29 | 显示全部楼层
围观......看不懂

出0入8汤圆

发表于 2014-9-29 19:33:57 来自手机 | 显示全部楼层
carolxr 发表于 2014-9-29 17:21
这种用法很常见啊。
unsigned char   array[21]定义了一个char类型的数组,长度是21,后面的几个结构体都是 ...

好处就是带来这种灵活方便的处理,但命名最好采用类似匈牙利命名,成员命名要注意用户体验,让人单看变量名称,就知道这是联合体。。。否则,这种写法,还不如直接取结构体的指针操作直观。

出0入0汤圆

发表于 2014-9-29 19:58:42 | 显示全部楼层
这种定义方式就是为了操作方便,多个复用同一个内存段(联合)就是为了省内存。
选用不同的结构体名,是为了在不同的代码进行区分,如全用一个变量名,有人会晕。

单片机中运行的读取操作,数据如果是字节数组形式的会比较方便,可以编写一些通用的函数来处理,为自己偷懒。

出0入0汤圆

 楼主| 发表于 2014-9-29 20:50:59 | 显示全部楼层
carolxr 发表于 2014-9-29 17:21
这种用法很常见啊。
unsigned char   array[21]定义了一个char类型的数组,长度是21,后面的几个结构体都是 ...

那使用union时除了需要注意串值还需要关注一些什么事项,因为现在这个项目很容易运行出错。不得不把可能的地方都考虑到

出0入0汤圆

 楼主| 发表于 2014-9-29 20:52:12 | 显示全部楼层
TANK99 发表于 2014-9-29 19:58
这种定义方式就是为了操作方便,多个复用同一个内存段(联合)就是为了省内存。
选用不同的结构体名,是为了 ...

学习了,查查相关资料看看

出0入0汤圆

发表于 2014-9-29 21:35:56 | 显示全部楼层
围观,学习

出0入0汤圆

发表于 2014-9-29 21:41:44 | 显示全部楼层
学习~~~~~~

出0入0汤圆

发表于 2014-9-29 21:55:43 | 显示全部楼层
很详细,谢谢                     

出100入85汤圆

发表于 2014-9-29 22:49:57 | 显示全部楼层
这肯定是个高手写的代码。

出0入0汤圆

发表于 2014-9-29 23:02:45 | 显示全部楼层

围观。。。。。。
结构体,联合体,数组应还有更强大的组合,求高手普及知识。。。。

出10入46汤圆

发表于 2014-9-30 08:46:45 | 显示全部楼层
结构长度一致。就OK

出0入0汤圆

发表于 2014-9-30 09:02:28 | 显示全部楼层
闲下来优化的时候这样可以,赶进度的时候不见得好用,还是老实的传递指针比较明白。

出0入0汤圆

发表于 2014-9-30 09:06:26 | 显示全部楼层
数据结构的优化,不仅仅是省内存这一点好处,有时候对算法效率的提升不是一点两点.

出0入0汤圆

发表于 2014-9-30 09:10:28 | 显示全部楼层
方便程序设计,尤其在数据处理比较多的时候,效率很高;经常用

出0入0汤圆

发表于 2014-9-30 09:13:37 | 显示全部楼层
对不同属性的多个变量同时操作时,就很方便了。比用指针直观。且很方便进行数据的增减。
比如把上面结构内的数据存入FLASH或从FLASH读出来的时候,对数组操作,在通过结构提取。

出0入0汤圆

发表于 2014-9-30 09:16:04 | 显示全部楼层
这样写,定义时虽然麻烦了点,但是程序中在使用,是非常方便的。飞思卡尔MCU的寄存器头文件,好像也都是这样写的,

出0入0汤圆

发表于 2014-9-30 09:24:03 | 显示全部楼层
省内存,数据存取效率高,
隐含数据处理,比如8bit位移操作。有些32bit/64bit数据声明时都弄成联合。

出0入0汤圆

发表于 2014-9-30 09:26:56 | 显示全部楼层
高手都这么写

出0入0汤圆

发表于 2014-9-30 09:43:35 | 显示全部楼层
几个结构体共享内存,没什么问题,比如同一内存可按整数操作和按字节操作,基本用法

出0入4汤圆

发表于 2014-9-30 09:47:18 | 显示全部楼层
高手的代码,对数据操作灵活。

出0入0汤圆

发表于 2014-9-30 09:50:02 | 显示全部楼层
这样写转换时比较清晰方便

出0入0汤圆

发表于 2014-9-30 09:57:43 | 显示全部楼层
好处多多啊,比如用于flash 存储,串口数据发送。

出0入0汤圆

发表于 2014-9-30 12:42:10 | 显示全部楼层
TANK99 发表于 2014-9-29 19:58
这种定义方式就是为了操作方便,多个复用同一个内存段(联合)就是为了省内存。
选用不同的结构体名,是为了 ...

同意此观点

出0入0汤圆

发表于 2014-9-30 12:54:08 | 显示全部楼层
方便操作,他估计也会交叉着用,直接强制转换来引用

出0入0汤圆

发表于 2014-9-30 16:47:19 | 显示全部楼层
学习了,顶一个!

出0入0汤圆

发表于 2014-9-30 17:50:43 来自手机 | 显示全部楼层
3楼正解,解析包的时候就知道它的好处了

出0入0汤圆

 楼主| 发表于 2014-11-3 23:19:40 | 显示全部楼层
carolxr 发表于 2014-9-29 17:21
这种用法很常见啊。
unsigned char   array[21]定义了一个char类型的数组,长度是21,后面的几个结构体都是 ...

现在也意识到这个方法的好处了。
简单通信时候用这个方法整合一条协议,操作起来很方便。

出0入0汤圆

发表于 2014-11-4 07:50:40 来自手机 | 显示全部楼层
一直这样用
要注意的是使用一些库函数操作字符串的时候结尾没有结束符,所以定义时应比你最大长度还要大一个。这个问题值得你注意。

出0入0汤圆

发表于 2014-11-4 08:12:08 | 显示全部楼层
这种用法很灵活啊。

出0入0汤圆

发表于 2014-11-4 08:28:04 | 显示全部楼层
好处不要太多啊;  

多用结构体,尤其是和联合体一起用,多用函数指针, 这样C水平才能前进一步啊;

出0入0汤圆

发表于 2014-11-4 09:27:20 | 显示全部楼层
看到不懂的进来学习一下

出0入0汤圆

发表于 2014-11-4 09:39:34 | 显示全部楼层
用的时候就知道了,如果只是数组就麻烦大了,每次都记身份证号的难度,大于记名字啊。

出0入0汤圆

发表于 2014-11-4 13:41:48 来自手机 | 显示全部楼层
学习了!

出0入0汤圆

发表于 2014-11-5 13:23:07 | 显示全部楼层
从没用过这么高深的东东啊

出0入0汤圆

发表于 2014-11-5 20:24:38 | 显示全部楼层
IAR一些头文件好像是这么写的。

出0入0汤圆

发表于 2014-11-5 21:46:02 | 显示全部楼层
多谢楼主分享

出0入0汤圆

 楼主| 发表于 2014-11-5 22:17:03 | 显示全部楼层
最近学习到一个

出0入0汤圆

发表于 2014-11-5 22:40:53 | 显示全部楼层
学习,我也是这么用。最常用的就是AD,不用左移右移的

出0入0汤圆

 楼主| 发表于 2014-11-5 22:52:09 | 显示全部楼层
本帖最后由 blavy 于 2014-11-5 22:53 编辑
ycping 发表于 2014-11-5 22:40
学习,我也是这么用。最常用的就是AD,不用左移右移的


我是用来做按键扫描
void Key_Read()                        
{
        uint8_t ReadData;
        press_time = 0x3fe;
        ReadData =~ keyconvert();
        while(--press_time)
                  Trg = ReadData & (ReadData ^ Cont);       
        Cont = ReadData;
}

uint8_t keyconvert()        
{       
        uint8_t Key_num;
        PORT portkey;
        portkey.BITS.B0 = KEY1;
        portkey.BITS.B1 = KEY2;
        portkey.BITS.B2 = KEY3;
        portkey.BITS.B3 = 1;
        portkey.BITS.B4 = 1;
        portkey.BITS.B5 = 1;
        portkey.BITS.B6 = 1;
        portkey.BITS.B7 = 1;
        Key_num= portkey.byte;
        return Key_num;
        }
然后用一个switch扫描按键,可做短按长按,非常方便

出0入0汤圆

发表于 2014-11-5 23:01:23 | 显示全部楼层
进来学习一下,以前从未这样用过。

出0入0汤圆

发表于 2014-11-5 23:10:32 来自手机 | 显示全部楼层
carolxr 发表于 2014-9-29 17:21
这种用法很常见啊。
unsigned char   array[21]定义了一个char类型的数组,长度是21,后面的几个结构体都是 ...

正解,面向对象编程

出40入42汤圆

发表于 2014-11-5 23:21:07 来自手机 | 显示全部楼层
结构体和联合体的相互内嵌,根据不同功能而不同,最近用过结构内嵌联合体的

出0入0汤圆

发表于 2014-11-7 01:00:51 | 显示全部楼层
blavy 发表于 2014-11-5 22:52
我是用来做按键扫描
void Key_Read()                        
{

这样也行呀,我只用它做过浮点数转4个字节byte。

出0入0汤圆

发表于 2014-11-7 01:10:57 来自手机 | 显示全部楼层
这个太正常了,有很多这么用的。

出0入0汤圆

发表于 2014-11-7 01:11:15 | 显示全部楼层
学习了

出0入0汤圆

发表于 2014-11-7 01:18:33 | 显示全部楼层
受教了 感谢指导

出0入0汤圆

发表于 2014-11-7 01:54:16 | 显示全部楼层
受教了 感谢指导

出0入0汤圆

发表于 2014-11-7 01:57:12 | 显示全部楼层
多谢楼主分享!

出0入0汤圆

发表于 2014-11-7 06:29:11 来自手机 | 显示全部楼层
省内存的做法,单片机中很常用

出0入0汤圆

发表于 2014-11-7 08:21:48 | 显示全部楼层
能读懂,会用,但自己不会主动这么写,怕出问题。。

出0入0汤圆

发表于 2014-11-7 08:27:37 | 显示全部楼层
这样写的话,我觉得移植性就不高了,因为联合中array的大小要和path_sol等其他结构体的大小一致,而其他结构体中的int,long等类型的长度在不同机器上是可变的。比如,当int是4个字节的时候,array的定义就要改。

出0入0汤圆

发表于 2014-11-7 08:47:50 | 显示全部楼层
学习了,做串口协议的时候,能用上了。。

出0入0汤圆

发表于 2014-11-7 08:58:57 | 显示全部楼层
不错,记号学习下

出0入0汤圆

发表于 2014-11-7 09:33:40 | 显示全部楼层
谢谢 也经常这么用

出0入0汤圆

发表于 2014-11-7 10:02:01 | 显示全部楼层
我也碰到同样一个项目,但是我是先看他写的程序,然后自己慢慢的学着写,现在已经搞定了那个项目

出0入0汤圆

发表于 2014-11-7 10:16:10 | 显示全部楼层
看来我也要努力考虑内存大小的使用

出0入0汤圆

发表于 2014-11-7 10:30:44 | 显示全部楼层
学习学习

出0入0汤圆

发表于 2014-11-7 11:15:44 | 显示全部楼层
联合体里面使用结构体 不仅可以节省内存 还能大大提高程序的运行效率

出0入0汤圆

发表于 2014-11-17 22:00:31 | 显示全部楼层
这个好处很多,可以让很多函数偶合性降低,方便编程。

出0入0汤圆

发表于 2014-11-17 22:01:23 | 显示全部楼层
很多时候传递参数只用指针就行了,不用各种外部变量了,。

出0入0汤圆

发表于 2014-11-18 10:37:01 | 显示全部楼层
能真正用好,好处多多。

出0入0汤圆

发表于 2014-11-18 11:39:05 | 显示全部楼层
受教了,从没想过这么用呢

出0入17汤圆

发表于 2014-11-18 14:16:23 | 显示全部楼层
baoya1 发表于 2014-11-7 10:16
看来我也要努力考虑内存大小的使用

这可不是为了节约内存哦,是为了方便操作。

出0入0汤圆

发表于 2014-11-18 14:19:55 | 显示全部楼层
围观。。。表示没用过

出0入0汤圆

发表于 2014-11-18 14:36:02 | 显示全部楼层
首先用一个数组也能实现相同的功能,但是使用数组就涉及到以后升级的话,改动会很大,因为可能很多index都要修改,而且这里面还用了不同的变量类型(unsigned long, unsigned char, unsigned int)更加难以控制;再有使用结构体的好处嘛,取成员操作方便,实现通信协议或类似的软件不容易出错。个人见解而已。

出0入0汤圆

发表于 2014-11-18 14:51:43 | 显示全部楼层
这种方法真是很好用,不是为了节省内存,为了方便数的操作,至少你可以用这样的方法来代替之前的左移右移!计算更快!

出0入0汤圆

发表于 2014-11-18 15:02:51 | 显示全部楼层
学习了!受教~!

出0入0汤圆

发表于 2014-11-18 15:10:06 来自手机 | 显示全部楼层
学习了,,,,

出0入0汤圆

发表于 2014-11-18 23:07:04 | 显示全部楼层
mark  学习了

出0入0汤圆

发表于 2014-11-18 23:27:05 | 显示全部楼层
长知识了,好写法!

出0入0汤圆

发表于 2014-11-24 16:20:54 | 显示全部楼层
围观.学习

出0入0汤圆

发表于 2014-12-1 20:16:49 | 显示全部楼层
这种方法不错,学习了,操作太方便了,

出0入0汤圆

发表于 2014-12-2 16:52:42 | 显示全部楼层
要注意对齐问题!
我原来定义的时候,没有注意对齐问题,结果有数据丢失。查了半天

出0入0汤圆

发表于 2014-12-2 16:54:56 | 显示全部楼层
这种定义舍内存,也好区分,增加程序的可读性啊.

出0入0汤圆

发表于 2014-12-2 17:22:40 | 显示全部楼层
hustsolo 发表于 2014-11-7 08:27
这样写的话,我觉得移植性就不高了,因为联合中array的大小要和path_sol等其他结构体的大小一致,而其他结 ...

所以又提现了一个stdint.h的重要性

出85入85汤圆

发表于 2014-12-2 17:32:30 | 显示全部楼层
mark,键盘扫描可以用得上

出0入0汤圆

 楼主| 发表于 2014-12-2 19:21:39 | 显示全部楼层
CaineStrong 发表于 2014-12-2 17:10
对于位的声明,要注意对齐。
之前这样定义
union mSol

对,后面有改掉了,这样还能更省内存,把那些用于补齐的空间也利用起来。

出0入0汤圆

发表于 2014-12-2 19:25:28 | 显示全部楼层
这个必须MARK啊,一直有这样的想法,但是不知道怎么弄,看到这个贴的回复恍然大悟啊

出0入0汤圆

发表于 2014-12-3 10:35:41 | 显示全部楼层
这样做的好处主要是方便数据操作,譬如牵涉到数据类型转换的时候,太方便了。

出0入0汤圆

发表于 2014-12-8 09:29:41 | 显示全部楼层
看到楼主可耻的头像,我可耻的硬了。

出0入0汤圆

发表于 2014-12-8 19:14:26 | 显示全部楼层
对于结构体,联合体什么的一直很头痛,现在也没闹明白。

出0入0汤圆

发表于 2014-12-8 19:25:08 | 显示全部楼层
学习了,这样处理数据缺失很方便

出0入0汤圆

发表于 2014-12-27 09:08:34 | 显示全部楼层
分场合   每一种设计思路都会有它的优点和缺点   要学会借鉴  灵活运用才是最好的
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-23 10:25

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表