网易首页 > 网易号 > 正文 申请入驻

编程学习过程中越是痛苦的时候,学到的东西就会越多

0
分享至

在STM32的RTC开发中,编程学习过程中越是痛苦的时候,学到的东西就会越多

1.结构体有何作用

三个月前,教研室里一个学长在华为南京研究院的面试中就遇到这个问题。当然,这只是面试中最基础的问题。如果问你你怎么回答?我的理解是这样的,C语言中结构体至少有以下三个作用:

(1)有机地组织了对象的属性。

比如,在STM32的RTC开发中,我们需要数据来表示日期和时间,这些数据通常是年、月、日、时、分、秒。如果我们不用结构体,那么就需要定义6个变量来表示。这样的话程序的数据结构是松散的,我们的数据结构最好是“高内聚,低耦合”的。所以,用一个结构体来表示更好,无论是从程序的可读性还是可移植性还是可维护性皆是:

typedef struct //公历日期和时间结构体

vu16 year;

vu8 month;

vu8 date;

vu8 hour;

vu8 min;

vu8 sec;

}_calendar_obj;

_calendar_obj calendar; //定义结构体变量

(2)以修改结构体成员变量的方法代替了函数(入口参数)的重新定义。

如果说结构体有机地组织了对象的属性表示结构体“中看”,那么以修改结构体成员变量的方法代替函数(入口参数)的重新定义就表示了结构体“中用”。继续以上面的结构体为例子,我们来分析。假如现在我有如下函数来显示日期和时间:

void DsipDateTime( _calendar_obj DateTimeVal)

那么我们只要将一个_calendar_obj这个结构体类型的变量作为实参调用DsipDateTime()即可,DsipDateTime()通过DateTimeVal的成变量来实现内容的显示。如果不用结构体,我们很可能需要写这样的一个函数:

void DsipDateTime( vu16 year,vu8 month,vu8 date,vu8 hour,vu8 min,vu8 sec)

显然这样的形参很不可观,数据结构管理起来也很繁琐。如果某个函数的返回值得是一个表示日期和时间的数据,那就更复杂了。这只是一方面。

另一方面,如果用户需要表示日期和时间的数据中还要包含星期(周),这个时候,如果之前没有用机构体,那么应该在DsipDateTime()函数中在增加一个形参vu8 week:

void DsipDateTime( vu16 year,vu8 month,vu8 date,vu8 week,vu8 hour,vu8 min,vu8 sec)

可见这种方法来传递参数非常繁琐。所以以结构体作为函数的入口参数的好处之一就是函数的声明void DsipDateTime(_calendar_obj DateTimeVal)不需要改变,只需要增加结构体的成员变量,然后在函数的内部实现上对calendar.week作相应的处理即可。这样,在程序的修改、维护方面作用显著。

typedef struct //公历日期和时间结构体

vu16 year;

vu8 month;

vu8 date;

vu8 week;

vu8 hour;

vu8 min;

vu8 sec;

}_calendar_obj;

_calendar_obj calendar; //定义结构体变量

(3)结构体的内存对齐原则可以提高CPU对内存的访问速度(以空间换取时间)。

并且,结构体成员变量的地址可以根据基地址(以偏移量offset)计算。我们先来看看下面的一段简单的程序,对于此程序的分析会在第2部分结构体成员变量内存对齐中详细说明。

#include

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.结构体成员变量内存对齐

首先,我们来分析一下上面程序的运行结果。前三行说明在我的程序中,char型占1个字节,short型占2个字节,long型占4个字节。char_short_long、long_short_char和char_long_short是三个结构体成员相同但是成员变量的排列顺序不同。并且从程序的运行结果来看,

Size of char_short_long = 8 bytes

Size of long_short_char = 8 bytes

Size of char_long_short = 12 bytes //比前两种情况大4 byte!

并且,还要注意到,1 byte (char)+ 2 byte (short)+ 4 byte (long) = 7 byte,而不是8 byte。

所以,结构体成员变量的放置顺序影响着结构体所占的内存空间的大小。一个结构体变量所占内存的大小不一定等于其成员变量所占空间之和。如果一个用户程序或者操作系统(比如uC/OS-II)中存在大量结构体变量时,这种内存占用必须要进行优化,也就是说,结构体内部成员变量的排列次序是有讲究的。

结构体成员变量到底是如何存放的呢?

在这里,我就不卖关子了,直接给出如下结论,在没有#pragma pack宏的情况下:

·原则1结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。

·原则2结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。

·原则3结构体作为成员时,结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素时,那么b应该从8的整数倍地址处开始存储,因为sizeof(double) = 8 bytes)

这里,我们结合上面的程序来分析(暂时不讨论原则3)。

先看看char_short_long和long_short_char这两个结构体,从它们的成员变量的地址可以看出来,这两个结构体符合原则1和原则2。注意,在 char_short_long的成员变量的地址中,char_short_long.s的地址是1244994,也就是说,1244993是“空的”,只是被“占位”了!

成员变量

成员变量十六进制地址

成员变量十进制地址

char_long_short.c

0x0012FF2C

char_long_short.l

0x0012FF30

char_long_short.s

0x0012FF34

可见,其内存分布图如下,共12 bytes:

首先,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)=2 bytes)是应该放在1244980处。

是不是这样就完毕了?不是,还有原则2。依据原则2的要求,char_long_short这个构造体所占的空间大小应该是其占内存空间最大的成员变量的大小的整数倍。假如我们到此就完毕了,那么char_long_short所占的内存空间是1244972~1244981共计10bytes,不合乎原则2,所以,必需在最后补齐2个 bytes(1244982~1244983)。

至此,一个构造体的内存布局完成了。

下面我们依照上述原则,来验证这样的分析是不是正确。按上面的分析,地址单元1244973、1244974、1244975以及1244982、1244983都是空的(至少char_long_short未用到,只是“占位”了)。假如我们的分析是正确的,那么,定义这样一个构造体,其所占内存也应该是12 bytes:

struct //声明构造体char_long_short_new

char c;

char add1; //补齐空间

char add2; //补齐空间

char add3; //补齐空间

long l;

short s;

char add4; //补齐空间

char add5; //补齐空间

}char_long_short_new;

可见,我们的分析是正确的。至于原则3,大家能够自己编程验证,这里就不再探讨了。

所以,没论你是在VC6.0还是Keil C51,还是Keil MDK中,当你须要定义一个构造体时,只有你稍微留心构造体成员变量内存对齐这一现象,就能够在很大程度上节约MCU的RAM。这一点不仅仅应用于实际编程,在很多大型公司,假如IBM、微软、百度、华为的笔试和面试中,也是常见的。

这三大块硬骨头是进修C语言的绊脚石,下功夫拿掉根本上C语言的大动脉就打通了,那么再去进修别的内容就相比照较简略了。编程进修过程中越是痛苦的时候,学到的东西就会越多,克服过去就会自己的技能,放弃了前面的付出的时长都将清零。越是难学的语言在入门之后,在入门之后越觉得过瘾,而且还容易上瘾。你上瘾了没?

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
李连杰利智上山修行120天,称为了世界和平,21岁小女儿乖巧陪同

李连杰利智上山修行120天,称为了世界和平,21岁小女儿乖巧陪同

开开森森
2024-06-16 07:24:44
楼市的救市方向,正在南辕北辙

楼市的救市方向,正在南辕北辙

听三哥说
2024-06-13 23:50:04
清华本硕,CPA+司考顶配的37岁金融男,被裁后竟然找不到工作,只求能还房贷…..

清华本硕,CPA+司考顶配的37岁金融男,被裁后竟然找不到工作,只求能还房贷…..

毯叔盘钱
2024-06-15 09:10:09
尿毒症是喝出来的?医生告诫:即便是铁打的肾,这3种水也要少喝

尿毒症是喝出来的?医生告诫:即便是铁打的肾,这3种水也要少喝

莫将离
2024-06-01 23:41:40
中国女排3-2土耳其,上演超级逆转,晋级总决赛,张常宁扮演奇兵

中国女排3-2土耳其,上演超级逆转,晋级总决赛,张常宁扮演奇兵

湘楚风云
2024-06-15 22:41:01
19分6板3助!巩晓彬儿子率队大胜:梦幻脚步+统治攻防,球商真高

19分6板3助!巩晓彬儿子率队大胜:梦幻脚步+统治攻防,球商真高

社会故事回忆录
2024-06-15 10:33:58
不能实现全民免费医疗的根源:只有看不起病的人才渴望免费医疗

不能实现全民免费医疗的根源:只有看不起病的人才渴望免费医疗

雪中风车
2024-06-10 12:51:04
谭咏麟病愈后首次公开现身,瘦到青筋毕现感慨声线不好

谭咏麟病愈后首次公开现身,瘦到青筋毕现感慨声线不好

小萝卜天下事
2023-07-21 21:57:53
难怪有人对父母感到心寒!看了网友的分享,心酸得想大哭一场

难怪有人对父母感到心寒!看了网友的分享,心酸得想大哭一场

镜头时光
2024-06-11 00:54:32
湖南:小伙捧鲜花表白女技师,做足疗一见钟情,网友:长得很哇塞

湖南:小伙捧鲜花表白女技师,做足疗一见钟情,网友:长得很哇塞

百晓史
2024-06-02 09:09:36
均衡,日本男女三大球代表队均已获得巴黎奥运会参赛资格

均衡,日本男女三大球代表队均已获得巴黎奥运会参赛资格

懂球帝
2024-06-15 15:49:11
美女模特,蜂腰大长腿,凹凸有致,请你吃晚饭你去不去

美女模特,蜂腰大长腿,凹凸有致,请你吃晚饭你去不去

傲娇的马甲线
2024-06-13 17:30:03
马来西亚总理:马来西亚加强对华关系是有道理的,因为中国“愿意接纳和倾听”

马来西亚总理:马来西亚加强对华关系是有道理的,因为中国“愿意接纳和倾听”

环球网资讯
2024-06-15 16:21:52
詹俊:克罗地亚中场三奇控传强可惜年龄偏大,无球正面防守偏弱

詹俊:克罗地亚中场三奇控传强可惜年龄偏大,无球正面防守偏弱

直播吧
2024-06-16 00:54:14
张天爱自拍大秀“凶器”,身材气质绝佳!曾和娜扎联手锤渣男?

张天爱自拍大秀“凶器”,身材气质绝佳!曾和娜扎联手锤渣男?

狐飞火
2024-06-15 00:40:03
美国总统拜登批准俄克拉何马州重大灾难声明

美国总统拜登批准俄克拉何马州重大灾难声明

财联社
2024-06-15 10:10:30
永久禁止出口欧美!拜登不淡定了,中国突然亮出关键“大杀器”

永久禁止出口欧美!拜登不淡定了,中国突然亮出关键“大杀器”

星辰故事屋
2024-06-11 19:23:42
凯特王妃重返公众视野,与王室成员的聊天被唇语解读

凯特王妃重返公众视野,与王室成员的聊天被唇语解读

土澳的故事
2024-06-15 23:09:09
6月15日,以军基地被炸,土耳其不宣而战,菲律宾船员弃船逃生

6月15日,以军基地被炸,土耳其不宣而战,菲律宾船员弃船逃生

笔墨V
2024-06-15 22:49:39
感觉刘亦菲在血洗内娱啊,这个才是真正健康美。

感觉刘亦菲在血洗内娱啊,这个才是真正健康美。

综艺拼盘汇
2024-06-15 18:42:49
2024-06-16 11:28:49
大方老师单片机
大方老师单片机
领单片机速学资料,点我头像~
259文章数 1144关注度
往期回顾 全部

科技要闻

iPhone 16会杀死大模型APP吗?

头条要闻

40余套房屋涉嫌"一房多卖" 有购房者内心积郁因病去世

头条要闻

40余套房屋涉嫌"一房多卖" 有购房者内心积郁因病去世

体育要闻

没人永远年轻 但青春如此无敌还是离谱了些

娱乐要闻

江宏杰秀儿女刺青,不怕刺激福原爱?

财经要闻

打断妻子多根肋骨 上市公司创始人被公诉

汽车要闻

东风奕派eπ008售21.66万元 冰箱彩电都配齐

态度原创

教育
本地
手机
公开课
军事航空

教育要闻

计算机专业,会是下一个土木吗?

本地新闻

粽情一夏|海河龙舟赛,竟然成了外国人的大party!

手机要闻

这次稳妥了,小米15系列手机发布时间、配置信息都有了准信

公开课

近视只是视力差?小心并发症

军事要闻

普京提停火和谈条件 美防长迅速回应

无障碍浏览 进入关怀版