第七节 结构体与联合体
来源:互联网 发布:centos下安装chrome 编辑:程序博客网 时间:2024/05/18 01:10
第七节 结构体与联合体
1.1 结构体内存对齐问题
1.1 结构体在STM32的应用
1.1 结构体在联合体中的声明
1.1 结构体内存对齐问题
1. 这个程序本是我写来验证结构体内存对齐问题,但是我在linux系统和windows系统下的答案让我有点意外,我便将其加进本书。如程序清单7. 1所示,程序输出会是什么?
程序清单7. 1 结构体的内存对齐问题
#include<stdio.h>
struct Date{
int year ;
int month ;
int day ;
} ;
struct DateType{
int year ;
int month ;
int day ;
}birthday ;
struct student{
int iNum ;
char cName[30] ;
float fScore ;
char cSex ;
double menber ;
} people ;
int main( int argc , char *argv[] )
{
printf( "sizeof(struct Date) = %d \n\n",
sizeof( struct Date) ) ;
printf( "sizeof(struct DateType) = %d \n" ,
sizeof( struct DateType) ) ;
printf( "sizeof(birthday) = %d \n\n", sizeof( birthday ) ) ;
printf( "&birthday.year = %d \n" , &birthday.year ) ;
printf( "&sizeof.month = %d \n" , &birthday.month ) ;
printf( "&sizeof.day = %d \n\n", &birthday.day ) ;
printf( "sizeof(struct student) = %d \n" ,
sizeof( struct student) ) ;
printf( "sizeof(people) = %d \n\n", sizeof( people ) ) ;
printf( "&people.iNum = %d \n" , &people.iNum ) ;
printf( "&people.cName = %d \n" , &people.cName ) ;
printf( "&people.fScore = %d \n" , &people.fScore ) ;
printf( "&people.cSex = %d \n" ,
&people.cSex ) ;
printf( "&people.menber = %d \n\n", &people.menber ) ;
printf( "sizeof(people.menber) = %d \n\n", sizeof( people.menber ) ) ;
return 0 ;
}
传统在windows下,结果大家都应该知道,我现在就直接把window下和linux下结果直接贴出来,大家看看。(如果大家对结果有质疑,大可上机试试,毕竟眼见为实。)
sizeof(struct Date) = 12
sizeof(struct DateType) = 12
sizeof(birthday) = 12
&birthday.year = 4210832
&sizeof.month = 4210836
&sizeof.day = 4210840
sizeof(struct student) = 56
sizeof(people) = 56
&people.iNum = 4210848
&people.cName = 4210852
&people.fScore = 4210884
&people.cSex = 4210888
&people.menber = 4210896
sizeof(people.menber) = 8
请按任意键继续. . .
上面是C-Free中运行的结果,你可以试试VC等,答案依然如此。
我们再来看看linux下结果:
root@zhuzhaoqi-desktop:/home/zhuzhaoqi/C/prog1.34# ./prog
sizeof(struct Date) = 12
sizeof(struct DateType) = 12
sizeof(birthday) = 12
&birthday.year = 134520948
&sizeof.month = 134520952
&sizeof.day = 134520956
sizeof(struct student) = 52
sizeof(people) = 52
&people.iNum = 134520896
&people.cName = 134520900
&people.fScore = 134520932
&people.cSex = 134520936
&people.menber = 134520940
sizeof(people.menber) = 8
这是linux下编译的结果。
加粗标注区域够让你吃惊吧!
说实话,看到第一眼,我也傻了。为什么,我们再看下划线标注区域,people.cSex 在windows下联系上下确实应该占用8个字节,可是,可是为什么在linux下只占用4个字节!!
原来,在linux中以4个字节为开辟单元,即不足4个开辟4个,多于4个的继续开辟4个,多出的部分 放进另一个4个字节中。
struct student{
int iNum ; /* 开辟4个字节 */
char cName[30] ; /* 开辟32个字节 */
float fScore ; /* 开辟4个字节 */
/*开辟4个字节,自己用1个字节,剩下3个,不足以存储menber */
char cSex ;
double menber ; /* 所以这里重新开辟4+4个字节 */
} people ;
所以我们得出的答案是:4+32+4+4+8=52。
但是,我们一直使用的windows下,以最大单元为开辟单位,即系统先检查结构中最大单位 为double 8个字节,所以以8个字节为单位。
student 在Linux和windows下内存开辟如图7. 1和图7. 2所示。
1.1 结构体在STM32的应用
1. 如程序清单7. 2所示程序是截取STM32固件库中的一段代码,请问输出是什么?
程序清单7. 2 结构体在STM32的应用
#include <stdio.h>
typedef volatile unsigned int vui32;
typedef struct {
vui32 CRL;
vui32 CRH;
vui32 IDR;
vui32 ODR;
vui32 BSRR;
vui32 BRR;
vui32 LCKR;
} GPIO_TypeDef;
#define GPIOA (GPIO_TypeDef *)0x10000000
#define GPIOLED (GPIO_TypeDef *)GPIOA
void func (GPIO_TypeDef *GPIO)
{
printf("GPIO->CRL = %#x\n" , &(GPIO->CRL));
printf("GPIO->CRH = %#x\n" , &(GPIO->CRH));
printf("GPIO->LCKR = %#x\n" , &(GPIO->LCKR));
}
int main(int argc, char *argv[])
{
printf("sizeof(GPIO_TypeDef) = %d\n" , sizeof(GPIO_TypeDef)) ;
printf("GPIOLED=%#x\n" , GPIOLED);
func(GPIOLED);
return 0;
}
如果使用过STM32的固件函数库的话,应该对这个结构体不会陌生,STM32固件函数库就是这样,通过“大行其肆”的结构体和指针实现对一大堆寄存器的配置,在_map.h这个头文件中,定义很多这样的结构体。这样做有什么好处呢,将共同点给抽象出来了,上面7个寄存器就是每个GPIO口寄存器的共有特性,那么只要给定某一个GPIO口的映射地址,很快就可以通过这个结构体得到每个寄存器的地址。能这么做很巧的是ARM的MCU每个寄存器的偏移量都是4个字节或者2个字节,所以能使用结构体完成,如果有一天出现了3个字节的偏移量,我想此时结构体也就没辙了。
答案是:
sizeof(GPIO_TypeDef) = 28
GPIOLED =0x10000000
GPIO->CRL = 0x10000000
GPIO->CRH = 0x10000004
GPIO->LCKR = 0x10000018
请按任意键继续. . .
确实很巧妙,方便!
1.2 结构体与指针
1. 已知如下所示条件。
struct student{
long int num ;
char *name ;
short int date ;
char sex ;
short int da[5] ;
}*p;
p = (student*)0x1000000 ;
那么请问,以下输出什么?
printf( "sizeof(*p) = %d\n" , sizeof(*p) ) ;
printf( "sizeof(student) = %d\n" , sizeof(student) ) ;
printf( "p = %#x\n" , p ) ;
printf( "p + 0x200 = %#x\n" , p + 0x200 ) ;
printf( "(char*)p + 0x200 = %#x\n" , (char*)p + 0x200 ) ;
printf( "(int*)p + 0x200 = %#x\n" , (int*)p + 0x200 ) ;
第一个输出不解释,内存对齐问题,结构体指针,答案为:24。
第二个输出答案为:24。
第三个输出,为已知,答案为:0x1000000。
第四个输出,由于p此时是结构体类型指针,那么
p+0x200 = p + 0x200*sizeof(student)。
所以 p + 0x200 = p + 0x200 * 24 = 0x1000000 + 0x3000 = 0x1003000。
第五个输出,由于p被强制转换成了字符型指针,那么p + 0x200 = 0x1000200。
第六个输出同理为:p + 0x200 = 0x1000800。
1.3 联合体的存储
1. 如程序清单7. 3所示,程序输出什么?
程序清单7. 3 联合体的存储
union {
int i ;
struct {
char L;
char H;
}Bity;
}N;
int main(int argc, char* argv[])
{
N.i = 0x1234;
printf("N.Bity.L = %#x\n",N.Bity.L);
printf("N.Bity.H = %#x\n",N.Bity.H);
return 0;
}
结构体的成员是共用一块内存,也就是说N.i和N.Bity是在同一个地址空间中。那么好办了,但是要注意,CPU是小段存储模式,所以低字节存储在低地址中,高字节存储在高地址中。那么N.Bity.L是取了低地址,也就是得到低字节,即为0x34,N.Bity.H是取了高字节,即为0x12。在电脑中,int是占4字节,所以存储方式如图10. 3所示。
其实这里有一个很巧妙的用法可以用于C51单片机中,为了与上面不重复,假设C51的存储模式是大端模式。在C51的定时器,给定时器赋初值的时候,要将高八位和低八位分别赋给模式1定时器的高位预置值和低位预置值,有这么个式子:
THx = (65536-10000)/256;
TLx = (65536-10000)%256.
那么我们就可以让这样写这个程序
union {
unsigned int i ;
struct {
unsigned char H;
unsigned char L;
}Bity;
}N;
int main(int argc, char* argv[])
{
……
N.i = 65536 - 10000;
THx = N.Bity.H ;
TLx = N.Bity.L ;
……
return 0;
}
这样很方便并且高效地将高低位置好,其实单片机是一个很好学习C语言的载体,麻雀虽小,但是五脏俱全。
65536 – 10000 = 55536 = 0xD8F0。
由于在单片机中,int是占2字节,那么存储方式如图7. 4所示。
1.1 结构体在联合体中的声明
1. 如程序清单7. 4所示,请问:printf( "%d\n" , sizeof(T.N) ) ;
printf( "%d\n" , sizeof(T) ) ;输出什么?
程序清单7. 4 结构体在联合体中的声明
union T {
int i ;
struct N {
int j ;
float k ;
double m ;
};
};
第一个结构体嘛,4+4+8=16;第二个嘛,最大元素所占内存开辟,则为:16!真的是这样吗?正确答案是:16,4!
union T {
int i ;
struct N {
int j ;
float k ;
double m ;
}A;
};
如果这个程序是这样,输出又是多少呢?正确答案是:16,16!不知道你是否知道其中的原因了呢?
1.2 结构体与联合体的内存
1. 如程序清单7. 5所示,语句printf("%d",sizeof(struct date)+sizeof(max));的执行结果是?
程序清单7. 5 结构体与联合的内存
typedef union {
long i;
int k[5];
char c;
} DATE;
struct data {
int cat;
DATE cow;
double dog;
}too;
DATE max;
很明显,这里考查的是联合体和结构体的内存对齐问题。联合体的内存大小是以最大类型存储内存作为依据,而结构体则是内存对齐相加。
上面例子的联合体最大是:20,所以sizeof(max) = 20 ;在结构体中,sizeof(cat) = 4 ,sizeof(cow) = 20 ,sizeof(dog) = 8 ,由于内存都是对齐的,所以siezof(struct date) = 32 .所以最终答案是:52.
#include <stdio.h>
typedef union {
long int i ;
int k[5] ;
char c ;
}DATE ;
struct data {
int cat ;
DATE cow ;
double dog ;
}too ;
int main(int argc, char *argv[])
{
DATE max;
printf("sizeof(cat)=%d\n", sizeof(too.cat));
printf("sizeof(cow)=%d\n", sizeof(too.cow));
printf("sizeof(dog)=%d\n\n", sizeof(too.dog));
printf("sizeof(struct data)=%d\n",sizeof(struct data));
printf("sizeof(max)=%d\n", sizeof(max));
printf("sizeof(struct data)+ sizeof(max)=%d\n",
sizeof(struct data)+ sizeof(max));
return 0;
}
运行结果是:
sizeof(cat)=4
sizeof(cow)=20
sizeof(dog)=8
sizeof(struct data)=32
sizeof(max)=20
sizeof(struct data)+ sizeof(max)=52
请按任意键继续. . .
内存对齐问题是一个比较难以理解的内存问题,因为摸不着、难以猜透。
1.3 再论联合体与结构体
1. 如程序清单7. 6所示,程序输出什么?
程序清单7. 6 再论联合体与结构体
union {
struct {
unsigned char c1:3;
unsigned char c2:3;
unsigned char c3:2;
}s;
unsigned char c;
}u;
int main(int argc, char *argv[])
{
u.c = 100;
printf("u.s.c1 = %d\n", u.s.c1);
printf("u.s.c2 = %d\n", u.s.c2);
printf("u.s.c3 = %d\n", u.s.c3);
return 0;
}
这个程序考查对结构体和联合体的理解。
首先我们应该知道,u.c和u.s是在同一个地址空间中,那么u.s中存储的数据即为100。100转化为二进制为:0110 0100。由于是小端模式存储方式,那么u.s.c1取最低三位100,即为十进制的4;u.s.c2取中间三位100,即为十进制的4;而u.s.c3取最高两位01,即为十进制的1。所以输出即为:4,4,1。
- 第七节 结构体与联合体
- 结构体与联合体的用法
- C语言中的联合体与结构体
- 联合体与结构体的区别
- 联合体union 与结构体struct
- C语言-结构体与联合体
- C语言结构体与联合体
- 【C语言常识】结构体与联合体
- 结构体和联合体
- 结构体和联合体
- 结构体、联合体复习
- 结构体,联合体
- C++结构体、联合体
- 结构体和联合体
- 结构体和联合体
- 结构体、联合体、枚举
- sizeof(结构体/联合体)
- 结构体、联合体、枚举
- air 学习笔记:读取文件
- 第六节 数组与指针
- java学习17-面相对象(多态)
- akka actor远程传递对象
- 分享一个dedecms自定义获取上级栏目名称和链接的标签
- 第七节 结构体与联合体
- 算法练习之约瑟夫环
- 第八节 内存分配与内存释放
- 第九节 笔试中的几个常考题
- 第十节 数据结构之冒泡排序、选择排序
- 二叉树遍历的递归和非递归实现
- 第十一节 机试题之数据编码
- 第十二节 机试题目之十进制1~N的所有整数中出现“1”的个数
- 第十三节 机试题之 遍历单链表一次,找出链表中间元素