在嵌入式系统中,一般不建议使用union结构,因为union结构中的各个成员之间存在相互影响,容易滋生问题。可见,union也是把双刃剑。懂得使用它的人可以做到“削铁如泥”,而不懂得使用它的人很可能会被其所伤。下面介绍的几种方法都是嵌入式系统常用的几种技巧。如果熟练掌握,将来定有所用。
1.all的使用
使用all的数据结构模型:
typedef _my_union
{
unsigned int all;
/*sizeof(my_union.my_struct)必须与sizeof(my_union.all)相等*/
struct
{
...
}my_struct;
}my_union;
----------EXAMPLE 1--------
嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。例如,32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址 | 存放内容 |
0x4000 | 0x78 |
0x4001 | 0x56 |
0x4002 | 0x34 |
0x4003 | 0x12 |
而在Big-endian模式CPU内存中的存放方式则为:
内存地址 | 存放内容 |
0x4000 | 0x12 |
0x4001 | 0x34 |
0x4002 | 0x56 |
0x4003 | 0x78 |
联合体union的存放顺序是所有成员都从低地址开始存放,利用该特性就可以轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写。
测试代码:
int isLittleEndian() { union _dword { int all; struct _bytes { char byte0;/*对little来说是最低位,而对big则是最高位*/ char pad[3]; }bytes; }dword; dword.all=0x87654321;/*all的功能就是给dword四个字节赋值*/ return (0x21==dword.bytes.byte0);/*查看第一个字节*/ }
分析: 如果你的处理器调用函数isLittleEndian返回1,那么说明你的处理器为little endian,否则为big endian.注意,如果在little endian处理器上,byte0和pad按内存从低到高的存放顺序:LOW->byte0 pad[0] pad[1] pad[2] ->HIGH;0x87654321按内存从低到高的存放顺序: 0x21 0x43 0x65 0x87, 可见byte0对应到0x21。所以通过判断dword中第一个字节dword.bytes.byte0是否与0x21相等就可以看出是否是little endian。
----------EXAMPLE 2--------
#include <stdio.h>
typedef union _student { unsigned int all;/*all可以同时清理或设置info中的所有内容*/ struct { unsigned int pad: 7; unsigned int used: 1;/*高位*/ unsigned int personal_id: 8; unsigned int class_id: 3; unsigned int subject_id: 6; unsigned int score: 7;/*低位*/ }info; }student;
#define MAX_STUDENT_NUM 100 unsigned int students; unsigned int student_database[MAX_STUDENT_NUM]={0};
int add_to_database(unsigned int data); int find_from_database(unsigned int personal_id); int delete_from_database(unsigned int personal_id); void print_database(void); void print_student(unsigned int data); int add_to_database(unsigned int data) { student stu; int i; for(i=0;i<MAX_STUDENT_NUM;i++) { stu.all = student_database[i]; if(!stu.info.used) { stu.all = data; stu.info.used = 1; student_database[i] = stu.all; return 1; } } return 0; } int find_from_database(unsigned int personal_id) { student stu; int i; for(i=0;i<MAX_STUDENT_NUM;i++) { stu.all = student_database[i]; if(stu.info.used && stu.info.personal_id==personal_id) { return stu.all; } } return -1; } void print_student(unsigned int data) { student stu; stu.all = data; printf("personal id %d,class id %d,subject id %d,score %d/n", stu.info.personal_id,stu.info.class_id,stu.info.subject_id,
stu.info.score); } void print_database(void) { student stu; int i; for(i=0;i<MAX_STUDENT_NUM;i++) { stu.all = student_database[i]; if(stu.info.used) { print_student(stu.all); } } } int main(int argc, char *argv[]) { student jack,jone; jack.all = 0; jack.info.personal_id = 102; jack.info.class_id = 2; /*class 2*/ jack.info.subject_id = 2; /*English*/ jack.info.score = 50; /*fouled*/
add_to_database(jack.all);
jone.all = 0; jone.info.personal_id = 88; jone.info.class_id = 2; /*calss 2*/ jone.info.subject_id = 2; /*English*/ jone.info.score = 73; /*passed*/ add_to_database(jone.all); jack.all = find_from_database(jack.info.personal_id); if(jack.all<0) { printf("no such student with id %d/n",jone.info.personal_id); } else { printf("found! "); print_student(jack.all); } print_database(); }
运行结果:
found! personal id 102,class id 2,subject id 2,score 50 personal id 102,class id 2,subject id 2,score 50 personal id 88,class id 2,subject id 2,score 73
在涉及音视频编解码算法中,经常会涉及一些数据压缩、声音解码、图象的缩放等问题。 这里通过一个例子来推荐一种union绝妙用法(这种方法由Equator公司提供,在我们公司的图象处理算法中用得很多)。在该例子中,利用union结构n64u实现占8个字节n64类型与单字节的c0~c7的相互转换,从而达到数据压缩和分解的目的。
#include <stdio.h>
#define IN #define OUT #define INOUT
typedef unsigned long long n64; typedef unsigned int n32; typedef unsigned short n16; typedef unsigned char n8;
typedef struct _s8t{ unsigned char c0, c1, c2, c3, c4, c5, c6, c7; }s8t;
/*利用n64和c0~c7占用同一内存区,实现它们之间的相互转换*/
typedef union { n64 n64;
struct { n32 l0, l1; } u32;
struct { long l0, l1; } s32;
struct { unsigned short s0, s1, s2, s3; } u16;
struct { short s0, s1, s2, s3; } s16;
struct { unsigned char c0, c1, c2, c3, c4, c5, c6, c7; } u8;
struct { char c0, c1, c2, c3, c4, c5, c6, c7; } s8; } n64u;
#define MAX_DATA_COMPRESSED_NUM 8
int compress64_8(IN const n64* src,IN n16 n,OUT n8* dst); int uncompress8_64(IN const n8* src,IN n16 n,OUT n64* dst);
int compress64_8(IN const n64* src,IN n16 n,OUT n8* dst) { n64u n64u_data; register n64* n64ptr=(n64*)src; register n8* n8ptr=dst; n16 i=0,num=n;
if(NULL==n64ptr || NULL==n8ptr || n<1) { printf("invalid param,src 0x%x,dst 0x%x,n %d/n",n64ptr,n8ptr,n); return 0; } for(i=0;i<num;i++) { n64u_data.n64 = *n64ptr++; /*用简单的均值法将8个点压缩成1个点*/ *n8ptr++ = (n64u_data.u8.c0+n64u_data.u8.c1+n64u_data.u8.c2+/ n64u_data.u8.c3+n64u_data.u8.c4+n64u_data.u8.c5+/ n64u_data.u8.c6+n64u_data.u8.c7)/(sizeof(n64)/sizeof(n8)); } return 1; }
int uncompress8_64(IN const n8* src,IN n16 n,OUT n64* dst) { n64u n64u_data; register n64* n64ptr=dst; register n8* n8ptr=(n8*)src; register n8 n8data; n16 i=0,num=n; if(NULL==n64ptr || NULL==n8ptr || n<1) { printf("invalid param,src 0x%x,dst 0x%x,n %d/n",n64ptr,n8ptr,n); return 0; } for(i=0;i<num;i++) { n8data=*n8ptr++; /*将1个点解压成8个点,采用简单的还原,所以有失真*/ n64u_data.u8.c0 = n8data; n64u_data.u8.c1 = n8data; n64u_data.u8.c2 = n8data; n64u_data.u8.c3 = n8data; n64u_data.u8.c4 = n8data; n64u_data.u8.c5 = n8data; n64u_data.u8.c6 = n8data; n64u_data.u8.c7 = n8data; *n64ptr++ = n64u_data.n64; } }
int main(int argc, char *argv[]) { n64 n64data[MAX_DATA_COMPRESSED_NUM]; n8 n8data[MAX_DATA_COMPRESSED_NUM]; s8t s8t_data[MAX_DATA_COMPRESSED_NUM] = {/*即将被压缩的数据,一个数据占一个字节*/ {1,2,3,4,5,6,7,8},/*8个字节被压缩成单个字节*/ {2,2,3,4,5,6,7,7}, {3,2,3,4,5,6,7,6}, {4,2,3,4,5,6,7,5}, {5,2,3,4,5,6,7,4}, {6,2,3,4,5,6,7,3}, {7,2,3,4,5,6,7,2}, {8,7,6,5,4,3,2,1} }; n16 i,n=MAX_DATA_COMPRESSED_NUM; printf("data:/n"); for(i=0;i<n;i++) { n64data[i] = *(n64*)&s8t_data[i]; printf("%3u %3u %3u %3u %3u %3u %3u %3u/n",
s8t_data[i].c0,s8t_data[i].c1,s8t_data[i].c2,
s8t_data[i].c3,s8t_data[i].c4,s8t_data[i].c5,
s8t_data[i].c6,s8t_data[i].c7); } printf("/n"); compress64_8(n64data,n,n8data); printf("compressed to:/n"); for(i=0;i<n;i++) { printf("%3u ",n8data[i]); } printf("/n/n"); uncompress8_64(n8data,n,n64data); printf("uncompressed to:/n"); for(i=0;i<n;i++) { *(n64*)&s8t_data[i] = n64data[i]; printf("%3u %3u %3u %3u %3u %3u %3u %3u/n",
s8t_data[i].c0,s8t_data[i].c1,s8t_data[i].c2,
s8t_data[i].c3,s8t_data[i].c4,s8t_data[i].c5,
s8t_data[i].c6,s8t_data[i].c7); } printf("/n"); }
/* 运行结果: data: 1 2 3 4 5 6 7 8 2 2 3 4 5 6 7 7 3 2 3 4 5 6 7 6 4 2 3 4 5 6 7 5 5 2 3 4 5 6 7 4 6 2 3 4 5 6 7 3 7 2 3 4 5 6 7 2 8 7 6 5 4 3 2 1
compressed to: 4 4 4 4 4 4 4 4
uncompressed to: 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 */
3.使不同数据包兼容
union的用法如下:
struct _my_struct { unsigned int struct_id
typedef union _my_union { struct my_struct_1; struct my_struct_2; struct my_struct_3;
}my_union;
}my_struct;
我们公司在处理音频视频数据流方面,为了区分音频和视频数据以及来自网络和编解码的数据并减少内存占用使用了下面的数据结构。这种union使用方法在网络应用中特别常见。在数据结构TFrameBufferInfo中,用bufferType标识数据来源。
/** /struct TABufferInfoFromCodec * /brief 来自编码器的音频数据的缓冲区信息 */ typedef struct { int codecId; /**< codec ID */ int packetNum; /**< 该帧数据一共有多少个包 */ int actualNum; /**< 当前已存入的包数 */ int packetLen; /**< 正常数据包最大长度,包括包头 */ int leftPacketLen; /**< 最后一个数据包大小,包括包头 */ int frameSample; /**< 0x00表示8kHz,0x01表示11kHz*/ int timeStamp; /**< 表示该帧的编码时间 */ int dataLen; /**< 数据长度,包括包头 */ int ready; /**< 该帧数据的数据包是否都已经写入*/ unsigned char* buffer; /**< 数据指针 */ int reserve1; /**< 保留位,用0填充 */
} TABufferInfoFromCodec;
/** /struct TVBufferInfoFromCodec * /brief 来自编码器的视频数据的缓冲区信息 */ typedef struct { int codecId; /**< codec ID */ int bKeyFrame; /**< TRUE:关键帧 */ int packetNum; /**< 该帧数据一共有多少个包 */ int actualNum; /**< 当前已存入的包数 */ int packetLen; /**< 正常数据包最大长度,包括包头 */ int leftPacketLen; /**< 最后一个数据包大小,包括包头 */ int timeStamp; /**< 表示该帧的编码时间 */ int dataLen; /**< 数据长度,包括包头 */ int ready; /**< 该帧数据的数据包是否都已经写入*/ unsigned char* buffer; /**< 数据指针 */ int reserve1; /**< 保留位,用0填充 */
} TVBufferInfoFromCodec;
/** /struct TVBufferInfoToCodec * /brief 来自网络的视频数据的缓冲区信息 */ typedef struct { int codecId; int bKeyFrame; int packetNum; int actualNum; int packetLen; int leftPacketLen; int rtpLen; int timeStamp; int firstSquence; int dataLen; int ready; unsigned char* buffer;
} TVBufferInfoToCodec;
/** /struct TABufferInfoToCodec * /brief 来自网络的音频数据的缓冲区信息 */ typedef struct { int codecId; int packetNum; int actualNum; int packetLen; int leftPacketLen; int rtpLen; int timeStamp; int firstSquence; int dataLen; int ready; unsigned char* buffer; int reserve1;
} TABufferInfoToCodec;
/** /struct TABufferInfoToCodec * /brief 数据缓冲区信息结构 */ typedef struct { int bufferType; union { TVBufferInfoFromCodec bufferInfoFromVCoder; TABufferInfoFromCodec bufferInfoFromACoder; TVBufferInfoToCodec bufferInfoFromVNetWork; TABufferInfoToCodec bufferInfoFromANetWork;
} buffer_info; } TFrameBufferInfo;
int send_to(void* stream);
int
send_to(void* stream) { /*省略*/ }
int main(int argc, char *argv[])
{
TFrameBufferInfo tFrameBufferInfo; TVBufferInfoFromCodec* pVBufferInfoFromCodec; unsigned char buffer[1200*5];/*最大存放5个数据包*/ /*假设你要向网络发送一段视频数据,音频接收为1, 视频接收为2,*音频发送为3,视频发送为4*/ tFrameBufferInfo.bufferType=4; pVBufferInfoFromCodec =&tFrameBufferInfo.buffer_info.bufferInfoFromVCoder; pVBufferInfoFromCodec->bKeyFrame = 1; pVBufferInfoFromCodec->packetNum = 2; pVBufferInfoFromCodec->actualNum = 2; pVBufferInfoFromCodec->leftPacketLen = 123; pVBufferInfoFromCodec->dataLen = 1323; pVBufferInfoFromCodec->ready = 1; pVBufferInfoFromCodec->buffer = buffer;
send_to((void*)&tFrameBufferInfo);
}