
相信大家对于结构体都不陌生。在此,分享出本人对C语言结构体的研究和学习的总结。如果你发现这个总结中有你以前所未掌握的,那本文也算是有点价值了。当然,水平有限,若发现不足之处恳请指出。代码文件test.c我放在下面。
在此,我会围绕以下2个问题来分析和应用C语言结构体:
1.C语言中的结构体有何作用
2.结构体成员变量内存对齐有何讲究(重点)
对于一些概念的说明,我就不把C语言教材上的定义搬上来。我们坐下来慢慢聊吧。
==============================================================================================================================================
1.结构体有何作用
(四)回答规定问题:考生由工作人员引导进入指定面试室,考官从题库中随机抽取2个规定问题,考生回答,时间5分钟。零基础,没有接触过教学也是第一次面试,7月15日开始系统复习,越是临近面试,感觉越迷糊,实际上是遇到了瓶颈期,这个阶段不知道如何突破自己,让自己更好,相信很多考生复习到一定阶段也遇到过类似的情况,遇到瓶颈,建议大家从头到尾梳理下自己的复习思路,把自己困惑的环节问题先解决掉,才能让自己更上一层。终面(par面):par面在南京,我是参加完立信par面回合肥的高铁上收到的面试,半路上在南京下车住了一夜参加的面试,1vs1单面, n个组同时进行,首先是自我介绍,其次是问题,par问我税法怎么样(应该是想问我税法的知识),然后我真诚坦白的说税法学的不怎么样,都忘记了,然后她就问了我两个会计的问题,分别是收入确认的条件和研发费用中资本化和费用化支出的确认(问题应该很多,par应该提前准备好很多问题,随机进行提问),接着就是围绕简历进行常规化的聊天式的面试,面试问题与瑞华、立信基本一致,没有需要特别需要注意的。
我的理解是这样的,C语言中结构体至少有以下三个作用:
(1)有机地组织了对象的属性。
比如,在STM32的RTC开发中,我们需要数据来表示日期和时间,这些数据通常是年、月、日、时、分、秒。如果我们不用结构体,那么就需要定义6个变量来表示。这样的话程序的数据结构是松散的,我们的数据结构最好是“高内聚,低耦合”的。所以,用一个结构体来表示更好,无论是从程序的可读性还是可移植性还是可维护性皆是:
typedefstruct//公历日期和时间结构体
{
vu16 year;
vu8 month;

vu8 date;
vu8hour;
vu8min;
vu8sec;
}_calendar_obj;
_calendar_objcalendar;//定义结构体变量
(2)以修改结构体成员变量的方法代替了函数(入口参数)的重新定义。
如果说结构体有机地组织了对象的属性表示结构体“中看”,那么以修改结构体成员变量的方法代替函数(入口参数)的重新定义就表示了结构体“中用”。继续以上面的结构体为例子,我们来分析。假如现在我有如下函数来显示日期和时间:
voidDsipDateTime(_calendar_objDateTimeVal)
那么我们只要将一个_calendar_obj这个结构体类型的变量作为实参调用DsipDateTime()即可,DsipDateTime()通过DateTimeVal的成变量来实现内容的显示。如果不用结构体,我们很可能需要写这样的一个函数:
voidDsipDateTime(vu16 year,vu8 month,vu8 date,vu8hour,vu8min,vu8sec)
显然这样的形参很不可观,数据结构管理起来也很繁琐。如果某个函数的返回值得是一个表示日期和时间的数据,那就更复杂了。这只是一方面。
最常见的表单控件, 基于文本输入字段. 支持包含html5在内的所有类型: 文本, 密码, 日期时间, 本地日期时间, 日期, 月, 时间, 星期, 数字, e-mail, url, 搜索, 电话, 和颜色.。h函数计算数月之前或之后的月末序列号1067应用month函数计算日期中的月份1078应用networkdays函数计算工作日的数值1089应用networkdays.intl函数返回两个日期之间的工作日天数10910应用today函数计算当前日期11111应用weekday函数计算日期为星期几11212应用weeknum函数计算某星期在一年中的星期数11313应用workday函数计算工作日之前。* 获得一个日期所在的周的星期几的日期,如要找出2002年2月3日所在周的星期一是几号。
if(date.getfullyear() == year && date.getmonth() == month-1&& date.getdate() == day){。fputs("if year(date)&month(date)&day(date)= ",fp)。scanf(“%d,%d,%d”,&date.year,&date.month,&date.day)。
可见这种方法来传递参数非常繁琐。所以以结构体作为函数的入口参数的好处之一就是

函数的声明voidDsipDateTime(_calendar_objDateTimeVal)不需要改变,只需要增加结构体的成员变量,然后在函数的内部实现上对calendar.week作相应的处理即可。这样,在程序的修改、维护方面作用显著。
typedefstruct//公历日期和时间结构体
{
vu16 year;
vu8 month;
vu8 date;
vu8 week;
vu8hour;
vu8min;
vu8sec;
}_calendar_obj;
_calendar_objcalendar;//定义结构体变量
(3)结构体的内存对齐原则可以提高CPU对内存的访问速度(以空间换取时间)。
并且,结构体成员变量的地址可以根据基地址(以偏移量offset)计算。我们先来看看下面的一段简单的程序,对于此程序的分析会在第2部分结构体成员变量内存对齐中详细说明。
}

#include<stdio.h>
int main()
{
struct //声明结构体char_short_long
{
char c;
short s;
long l;
}char_short_long;
struct //声明结构体long_short_char
{
long l;
short s;
char c;
}long_short_char;
struct //声明结构体char_long_short
{
char c;
long l;
short s;
}char_long_short;
printf(" \n");
printf(" Size of char = %d bytes\n",sizeof(char));
printf(" Size of shrot = %d bytes\n",sizeof(short));
printf(" Size of long = %d bytes\n",sizeof(long));
printf(" \n"); //char_short_long
printf(" Size of char_short_long = %d bytes\n",sizeof(char_short_long));
printf(" Addr of char_short_long.c = 0x%p (10进制:%d)\n",&char_short_long.c,&char_short_long.c);
printf(" Addr of char_short_long.s = 0x%p (10进制:%d)\n",&char_short_long.s,&char_short_long.s);
printf(" Addr of char_short_long.l = 0x%p (10进制:%d)\n",&char_short_long.l,&char_short_long.l);
printf(" \n");
printf(" \n"); //long_short_char
printf(" Size of long_short_char = %d bytes\n",sizeof(long_short_char));
printf(" Addr of long_short_char.l = 0x%p (10进制:%d)\n",&long_short_char.l,&long_short_char.l);
printf(" Addr of long_short_char.s = 0x%p (10进制:%d)\n",&long_short_char.s,&long_short_char.s);
printf(" Addr of long_short_char.c = 0x%p (10进制:%d)\n",&long_short_char.c,&long_short_char.c);
printf(" \n");
printf(" \n"); //char_long_short
printf(" Size of char_long_short = %d bytes\n",sizeof(char_long_short));
printf(" Addr of char_long_short.c = 0x%p (10进制:%d)\n",&char_long_short.c,&char_long_short.c);
printf(" Addr of char_long_short.l = 0x%p (10进制:%d)\n",&char_long_short.l,&char_long_short.l);
printf(" Addr of char_long_short.s = 0x%p (10进制:%d)\n",&char_long_short.s,&char_long_short.s);
printf(" \n");
return 0;
}
程序的运行结果如下(注意:括号内的数据是成员变量的地址的十进制形式):

2.结构体成员变量内存对齐
首先,我们来分析一下上面程序的运行结果。c 计算结构体大小前三行说明在我的程序中,char型占1个字节,short型占2个字节,long型占4个字节。char_short_long、long_short_char和char_long_short是三个结构体成员相同但是成员变量的排列顺序不同。并且从程序的运行结果来看,
Size of char_short_long =8bytes
Sizeof long_short_char=8bytes
Sizeof char_long_short=12bytes//比前两种情况大4byte!
int short long int short 16 1ong 32 int 16 32 shortint 16 long32 short intintlong signed unsigned char unsigned 02n n char 8 unsigned char 0255 signed char -128127 char long double floatdouble long double b 2-1 signedunsigned charshort int long 2。1. 整数 - 可应用于 java 的整数类型:byte、byte、short、short、int、integer、long、long 和 biginteger。基本数据类型包括byte、int、char、long、float、double、boolean和short。
所以,结构体成员变量的放置顺序影响着结构体所占的内存空间的大小。一个结构体变量所占内存的大小不一定等于其成员变量所占空间之和。如果一个用户程序或者操作系统(比如uC/OS-II)中存在大量结构体变量时,这种内存占用必须要进行优化,也就是说,结构体内部成员变量的排列次序是有讲究的。
结构体成员变量到底是如何存放的呢?
在这里,我就不卖关子了,直接给出如下结论,在没有#pragmapack宏的情况下:
原则1结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
原则2结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
*原则3结构体作为成员时,结构体成员要从其内部最大元素大小的整数倍地址开始存储。(structa里存有structb,b里有char,int,double等元素时,那么b应该从8的整数倍地址处开始存储,因为sizeof(double)=8bytes)

这里,我们结合上面的程序来分析(暂时不讨论原则3)。
先看看char_short_long和long_short_char这两个结构体,从它们的成员变量的地址可以看出来,这两个结构体符合原则1和原则2。注意,在char_short_long的成员变量的地址中,char_short_long.s的地址是1244994,也就是说,1244993是“空的”,只是被“占位”了!
再看看char_long_short这个结构体,char_long_short的地址分布情况如下表:
成员变量
成员变量十六进制地址
成员变量十进制地址
char_long_short.c
0x0012FF2C
char_long_short.l
0x0012FF30
char_long_short.s
0x0012FF34
可见,其内存分布图如下,共12bytes:
首先,1244972能被1整除,所以char_long_short.c放在1244972处没有问题(其实,就char型成员变量自身来说,其放在任何地址单元处都没有问题),根据原则1,在之后的1244973~1244975中都没有能被4(因为sizeof(long)=4bytes)整除的,1244976能被4整除,所以char_long_short.l应该放在1244976处,那么同理,最后一个.s(sizeof(short)=2bytes)是应该放在1244980处。
是不是这样就结束了?不是,还有原则2。根据原则2的要求,char_long_short这个结构体所占的空间大小应该是其占内存空间最大的成员变量的大小的整数倍。如果我们到此就结束了,那么char_long_short所占的内存空间是1244972~1244981共计10bytes,不符合原则2,所以,必须在最后补齐2个bytes(1244982~1244983)。
至此,一个结构体的内存布局完成了。
下面我们按照上述原则,来验证这样的分析是不是正确。c 计算结构体大小按上面的分析,地址单元1244973、1244974、1244975以及1244982、1244983都是空的(至少char_long_short未用到,只是“占位”了)。如果我们的分析是正确的,那么,定义这样一个结构体,其所占内存也应该是12bytes:
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-98524-1.html
脱肛真的不要成本