结构体与联合体
最近接手了别人做的一个项目,问题不少。在阅读程序的过程中看到其变量定义上的做法很是让我费解,也让我很郁闷。
他的做法如下,是在联合体里嵌套了多个结构体:
typedefunion_RUN_PARAM{
unsigned char array;
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的机率。
我不知道这个人这样做的目的何在,可能是为了省内存(因为公司想要马儿跑又要马儿不吃草,用资源很少的单片机来做太多的事),但是会想到省内存的人
不会不考虑到互相更改的可能。也许这样的写法有其巨大的好处但我确实看不出来,因为我也是遇到问题才去研究。
当然若有论坛牛人经过也请放缓脚步,为我这种新手普及一下知识。
此只是发发唠骚,不喜勿喷。 本帖最后由 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口里的一样。{:lol:} 对于位的声明,要注意对齐。
之前这样定义
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;
};
出现数据丢失。
才发现是对齐问题,改为下面的就好啦。惭愧惭愧。
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;
}; 省内存,可共享使用也可交替使用 这种用法很常见啊。
unsigned char array定义了一个char类型的数组,长度是21,后面的几个结构体都是跟这个数组共享同一个数据空间。
或者这么说,这段程序定义了一段内存空间,使用union结构体可以有多种方式去访问这段内存。你可以使用数组方式去访问,比如要对这段内存初始化清零的时候,使用数组就方便多了。同样的,如果你只需要访问内存中的某个字段,就用结构体的方式。定义多个结构体,可能是因为不同的对象,其属性不完全相同。
不好意思,说的不是很清楚。
单片机编程,本来就是要考虑节省内存,在哪里都一样,嵌入式编程就是这样,不比计算机编程,想怎么样就怎么样。
carolxr 发表于 2014-9-29 17:21
这种用法很常见啊。
unsigned char array定义了一个char类型的数组,长度是21,后面的几个结构体都是 ...
正解,这样写,对内存操作很灵活 他这个联合体定义了4个东西(一个数组+三个结构体),这样只是为了方便存取,并不是想拿这一份内存保持4份内容,实际也是不可能的。 学习了。。 围观………………{:smile:} 围观……………… 围观......看不懂 carolxr 发表于 2014-9-29 17:21
这种用法很常见啊。
unsigned char array定义了一个char类型的数组,长度是21,后面的几个结构体都是 ...
好处就是带来这种灵活方便的处理,但命名最好采用类似匈牙利命名,成员命名要注意用户体验,让人单看变量名称,就知道这是联合体。。。否则,这种写法,还不如直接取结构体的指针操作直观。 这种定义方式就是为了操作方便,多个复用同一个内存段(联合)就是为了省内存。
选用不同的结构体名,是为了在不同的代码进行区分,如全用一个变量名,有人会晕。
单片机中运行的读取操作,数据如果是字节数组形式的会比较方便,可以编写一些通用的函数来处理,为自己偷懒。 carolxr 发表于 2014-9-29 17:21
这种用法很常见啊。
unsigned char array定义了一个char类型的数组,长度是21,后面的几个结构体都是 ...
那使用union时除了需要注意串值还需要关注一些什么事项,因为现在这个项目很容易运行出错。不得不把可能的地方都考虑到 TANK99 发表于 2014-9-29 19:58
这种定义方式就是为了操作方便,多个复用同一个内存段(联合)就是为了省内存。
选用不同的结构体名,是为了 ...
学习了,查查相关资料看看 围观,学习 学习~~~~~~ 很详细,谢谢 这肯定是个高手写的代码。
围观。。。。。。
结构体,联合体,数组应还有更强大的组合,求高手普及知识。。。。 结构长度一致。就OK 闲下来优化的时候这样可以,赶进度的时候不见得好用,还是老实的传递指针比较明白。 数据结构的优化,不仅仅是省内存这一点好处,有时候对算法效率的提升不是一点两点. 方便程序设计,尤其在数据处理比较多的时候,效率很高;经常用 对不同属性的多个变量同时操作时,就很方便了。比用指针直观。且很方便进行数据的增减。
比如把上面结构内的数据存入FLASH或从FLASH读出来的时候,对数组操作,在通过结构提取。 这样写,定义时虽然麻烦了点,但是程序中在使用,是非常方便的。飞思卡尔MCU的寄存器头文件,好像也都是这样写的, 省内存,数据存取效率高,
隐含数据处理,比如8bit位移操作。有些32bit/64bit数据声明时都弄成联合。 {:titter:}高手都这么写 几个结构体共享内存,没什么问题,比如同一内存可按整数操作和按字节操作,基本用法 高手的代码,对数据操作灵活。 这样写转换时比较清晰方便 好处多多啊,比如用于flash 存储,串口数据发送。 TANK99 发表于 2014-9-29 19:58
这种定义方式就是为了操作方便,多个复用同一个内存段(联合)就是为了省内存。
选用不同的结构体名,是为了 ...
同意此观点 方便操作,他估计也会交叉着用,直接强制转换来引用 学习了,顶一个! 3楼正解,解析包的时候就知道它的好处了 carolxr 发表于 2014-9-29 17:21
这种用法很常见啊。
unsigned char array定义了一个char类型的数组,长度是21,后面的几个结构体都是 ...
{:victory:}现在也意识到这个方法的好处了。
简单通信时候用这个方法整合一条协议,操作起来很方便。 一直这样用
要注意的是使用一些库函数操作字符串的时候结尾没有结束符,所以定义时应比你最大长度还要大一个。这个问题值得你注意。 这种用法很灵活啊。 好处不要太多啊;
多用结构体,尤其是和联合体一起用,多用函数指针, 这样C水平才能前进一步啊; 看到不懂的进来学习一下 用的时候就知道了,如果只是数组就麻烦大了,每次都记身份证号的难度,大于记名字啊。 学习了! 从没用过这么高深的东东啊 IAR一些头文件好像是这么写的。 多谢楼主分享 最近学习到一个 学习,我也是这么用。最常用的就是AD,不用左移右移的 本帖最后由 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扫描按键,可做短按长按,非常方便 进来学习一下,以前从未这样用过。 carolxr 发表于 2014-9-29 17:21
这种用法很常见啊。
unsigned char array定义了一个char类型的数组,长度是21,后面的几个结构体都是 ...
正解,面向对象编程 结构体和联合体的相互内嵌,根据不同功能而不同,最近用过结构内嵌联合体的 blavy 发表于 2014-11-5 22:52
我是用来做按键扫描
void Key_Read()
{
这样也行呀,我只用它做过浮点数转4个字节byte。 这个太正常了,有很多这么用的。 学习了 受教了 感谢指导 受教了 感谢指导 多谢楼主分享! 省内存的做法,单片机中很常用 能读懂,会用,但自己不会主动这么写,怕出问题。。 这样写的话,我觉得移植性就不高了,因为联合中array的大小要和path_sol等其他结构体的大小一致,而其他结构体中的int,long等类型的长度在不同机器上是可变的。比如,当int是4个字节的时候,array的定义就要改。 学习了,做串口协议的时候,能用上了。。 不错,记号学习下 谢谢 也经常这么用 我也碰到同样一个项目,但是我是先看他写的程序,然后自己慢慢的学着写,现在已经搞定了那个项目 看来我也要努力考虑内存大小的使用 学习学习 联合体里面使用结构体 不仅可以节省内存 还能大大提高程序的运行效率 这个好处很多,可以让很多函数偶合性降低,方便编程。 很多时候传递参数只用指针就行了,不用各种外部变量了,。 能真正用好,好处多多。 受教了,从没想过这么用呢 baoya1 发表于 2014-11-7 10:16
看来我也要努力考虑内存大小的使用
这可不是为了节约内存哦,是为了方便操作。 围观。。。表示没用过 首先用一个数组也能实现相同的功能,但是使用数组就涉及到以后升级的话,改动会很大,因为可能很多index都要修改,而且这里面还用了不同的变量类型(unsigned long, unsigned char, unsigned int)更加难以控制;再有使用结构体的好处嘛,取成员操作方便,实现通信协议或类似的软件不容易出错。个人见解而已。 这种方法真是很好用,不是为了节省内存,为了方便数的操作,至少你可以用这样的方法来代替之前的左移右移!计算更快!{:victory:}{:victory:} 学习了!受教~! 学习了,,,, mark学习了 长知识了,好写法! 围观.学习 这种方法不错,学习了,操作太方便了, 要注意对齐问题!
我原来定义的时候,没有注意对齐问题,结果有数据丢失。查了半天{:cry:} 这种定义舍内存,也好区分,增加程序的可读性啊. hustsolo 发表于 2014-11-7 08:27
这样写的话,我觉得移植性就不高了,因为联合中array的大小要和path_sol等其他结构体的大小一致,而其他结 ...
所以又提现了一个stdint.h的重要性 mark,键盘扫描可以用得上 CaineStrong 发表于 2014-12-2 17:10
对于位的声明,要注意对齐。
之前这样定义
union mSol
{:handshake:} 对,后面有改掉了,这样还能更省内存,把那些用于补齐的空间也利用起来。 这个必须MARK啊,一直有这样的想法,但是不知道怎么弄,看到这个贴的回复恍然大悟啊 这样做的好处主要是方便数据操作,譬如牵涉到数据类型转换的时候,太方便了。 看到楼主可耻的头像,我可耻的硬了。 对于结构体,联合体什么的一直很头痛,现在也没闹明白。 学习了,这样处理数据缺失很方便 分场合 每一种设计思路都会有它的优点和缺点 要学会借鉴灵活运用才是最好的
页:
[1]