您的位置: 主页 > 产品中心 >

咨询热线

400-690-123455
  • 十大网赌网址嵌入式开发中C语言位结构体用途详
十大网赌网址嵌入式开发中C语言位结构体用途详

结构体的定义 结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构。 结构体和其他类型基础数据类型一样,例如int类型,

全国热线

400-690-123455

  • 产品详情

  结构体的定义  结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构。   结构体和其他类型基础数据类型一样,例如int类型,char类型只不过结构体可以做成你想要的数据类型。以方便日后的使用。   在实际项目中,结构体是大量存在的。研发人员常使用结构体来封装一些属性来组成新的类型。由于C语言无法操作

  例如一个学生的信息就需要学号(字符串),姓名(字符串),年龄(整形)等等。

  这些数据类型都不同但是他们又是表示一个整体,要存在联系,那么我们就需要一个新的数据类型。

  结构体在函数中的作用不是简便,其最主要的作用就是封装。封装的好处就是可以再次利用。让使用者不必关心这个是什么,只要根据定义使用就可以了。

  结构体的大小不是结构体元素单纯相加就行的,因为我们现在主流的计算机使用的都是32Bit字长的CPU,对这类型的CPU取4个字节的数要比取一个字节要高效,也更方便。所以在结构体中每个成员的首地址都是4的整数倍的话,取数据元素时就会相对更高效,这就是内存对齐的由来。每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragmapack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。

  1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragmapack指定的数值和这个数据成员自身长度中,比较小的那个进行。

  2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragmapack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

  3、结合1、2可推断:当#pragmapack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。

  在C++语言中,可以定义结构体类型,将多个相关的变量包装成为一个整体使用。在结构体中的变量,可以是相同、部分相同,或完全不同的数据类型。在C语言中,结构体不能包含函数。在面向对象的程序设计中,对象具有状态(属性)和行为,状态保存在成员变量中,行为通过成员方法(函数)来实现。C语言中的结构体只能描述一个对象的状态,不能描述一个对象的行为。在C++中,考虑到C语言到C++语言过渡的连续性,对结构体进行了扩展,C++的结构体可以包含函数,这样,C++的结构体也具有类的功能,与class不同的是,结构体包含的函数默认为public,而不是private。

  但是注意,它并没有创建一个实际的数据对象,而是描述了一个组成这类对象的元素。

  因此,我们有时候也将结构体声明叫做模板,因为它勾勒出数据该如何存储,并没有实例化数据对象。

  2、后面是一个可选的标志(book),它是用来引用该结构体的快速标记。       因此我们以后就可以这样创建数据对象

  3、接下来就是一个花括号,括起了结构体成员列表,及每个成员变量,使用的都是其自己的声明方式来描述,用分号来结束描述;

  例如:char title[MAXTITL];字符数组就是这样声明的,用分号结束; 注意:其中每个成员可以使用任何一种C数据结构甚至是其他的结构体,也是可以的;

  关于其struct声明的位置,也就是这段代码要放到哪里。同样这也是具有作用域的。

  这种声明如果放在任何函数的外面,那么则可选标记可以在本文件中,该声明的后面的所有函数都可以使用。

  如果这种声明在某个函数的内部,则它的标记只能在内部使用,并且在其声明之后;

  关于我们不断说的,标记名是可选的,那么我们什么时候可以省略,什么时候一定不能省略呢?

  如果是上面那种声明定义的方法,并且想在一个地方定义结构体设计,而在其他地方定义实际的结构体变量,那么就必须使用标记;

  struct 结构体名(也就是可选标记名){    成员变量;};//使用分号表示定义结束。

  之前我们结构体类型的定义(结构体的声明)只是告诉编译器该如何表示数据,但是它没有让计算机为其分配空间。

  看到这条指令,编译器才会创建一个结构体变量library,此时编译器才会按照book模板为该变量分配内存空间,并且这里存储空间都是以这个变量结合在一起的。

  在结构体声明中,struct book所起到的作用就像int,,,,等基础数据类型名作用一样。

  struct book s1,s2,*ss;  定义两个struct book结构体类型的结构体变量,还定义了一个指向该结构体的指针,其ss指针可以指向s1,s2,或者任何其他的book结构体变量。

  //注意这里不再是定义声明结构体类型,而是直接创建结构体变量了,这个编译器会分配内存的;

  //这样的确可以省略标识符也就是结构体名,但是只能使用一次;因为这是;声明结构体的过程和定义结构体变量的过程和在了一起;并且个成员变量没有初始化的;

  struct book s1,s2,*ss;//注意这种之前要先定义结构体类型后再定义变量;

  这种方式不能指明结构体类型名而是直接定义结构体变量,并且在值定义一次结构体变量时适用,无结构体名的结构体类型是无法重复使用的。

  也就是说,后面程序不能再定义此类型变量了,除非再写一次重复的struct。

  也是使用花括号括起来,用逗号分隔的初始化好项目列表,注意每个初始化项目必须要和要初始化的结构体成员类型相匹配。

  struct book s1={//对结构体初始化 yuwen,//title为字符串 guojiajiaoyun,//author为字符数组 22.5 //value为flaot型 };//要对应起来,用逗号分隔开来,与数组初始化一样;  加入一点小知识;关于结构体初始化和存储类时期的问题;如果要初始化一个具有静态存储时期的结构体,初始化项目列表中的值必须是常量表达式;

  注意如果在定义结构体变量的时候没有初始化,那么后面就不能全部一起初始化了;意思就是:

  结构体就像一个超级数组,在这个超级数组内,一个元素可以是char类型,下个元素就可以是flaot类型,再下个还可以是int数组型,这些都是存在的。

  在数组里面我们通过下标可以访问一个数组的各个元素,那么如何访问结构体中的各个成员呢?

  然后就可以像字符数组那样使用s1.title,像使用float数据类型一样使用s1.value;

  因此s1.value就相当于float类型的变量名一样,按照float类型来使用;

  注意scanf(“%d”, 这语句存在两个运算符,&和结构成员运算符点。

  按照道理我们应该将(s1。value括起来,因为他们是整体,表示s1的value部分)但是我们不括起来也是一样的,因为点的优先级要高于&。

  如果其成员本身又是一种结构体类型,那么可以通过若干个成员运算符,一级一级的找到最低一级成员再对其进行操作;

  可以将一个结构体变量作为一个整体赋值给另一相同类型的结构体变量,可以到达整体赋值的效果;这个成员变量的值都将全部整体赋值给另外一个变量;

  不能将一个结构体变量作为一个整体进行输入和输出;在输入输出结构体数据时,必须分别指明结构体变量的各成员;

  小结:除去“相同类型的结构体变量可以相互整体赋值”外,其他情况下,不能整体引用,只能对各个成员分别引用;    结构体长度

  char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)

  unsigned long:  4个字节  那么,下面这个结构体类型占几个字节呢?

  可以看到addr和name都只占一个字节,但是未满4字节,跳过2字节后才是id的值,这就是4字节对齐。结构体成员有int型,会自动按照4字节对齐。

  可见,结构体成员顺序优化,可节省空间。   如果全部成员都是char型,会按照1字节对齐,即

  先定义结构体类型PERSON,再定义结构体STUDENT,PERSON作为它的一个成员。

  按照前面的方法,打印各成员的值。1、定义STUDENT 指针变量指向数组ss

  age);printf(STUDENT长度=%d字节 ,sizeof(STUDENT));

  //对于“一锤子买卖”,只对最终的结构体变量感兴趣,其中A、B也可删,不过最好带着 struct A{ struct B{ int c; } b; } a; //使用如下方式访问:a.b.c = 10;  特别的,可以一边定义结构体B,一边就使用上:

  但是如果嵌套的结构体B是在A内部才声明的,并且没定义一个对应的对象实体b,这个结构体B的大小还是不算进结构体A中。

  (结构体长度、结构体字节对齐、结构体嵌套内容来源于公众号“0基础学单片机”,作者:森林木,感谢原作者的分享)

  占用内存空间  struct结构体,在结构体定义的时候不能申请内存空间,不过如果是结构体变量,声明的时候就可以分配——两者关系就像C++的类与对象,对象才分配内存(不过严格讲,作为代码段,结构体定义部分“.text”真的就不占空间了么?当然,这是另外一个范畴的话题)。

  结构体的大小通常(只是通常)是结构体所含变量大小的总和,下面打印输出上述结构体的size:

  下边说说不通常的情况  对于结构体中比较小的成员,可能会被强行对齐,造成空间的空置,这和读取内存的机制有关,为了效率。十大网赌网址,通常32位机按4字节对齐,小于的都当4字节,有连续小于4字节的,可以不着急对齐,等到凑够了整,加上下一个元素超出一个对齐位置,才开始调整,比如3+2或者1+4,后者都需要另起(下边的结构体大小是8bytes),相关例子就多了,不赘述。

  相应的,64位机按8字节对齐。不过对齐不是绝对的,用#pragma pack()可以修改对齐,如果改成1,结构体大小就是实实在在的成员变量大小的总和了。

  和C++的类不一样,结构体不可以给结构体内部变量初始化,。 如下,为错误示范:

  C++的结构体变量的声明定义和C有略微不同,说白了就是更“面向对象”风格化,要求更低。

  然后 handle_video() 函数根据视频的这些参数处理视频,之后 send_video() 负责将处理后的视频发送出去。下面是一次调用:

  虽说C语言程序的代码风格因人而异,但是“重复的代码”永远是应尽力避免的,原因本专栏已经分析多次。不管怎么说,每次使用这几个函数,都需要定义很多临时变量,总是非常麻烦的。所以,这种情况下,完全可以使用C语言的结构体语法:

  在嵌入式开发中,经常需要表示各种系统状态,位结构体的出现大大方便了我们,尤其是在进行一些硬件层操作和数据通信时。但是在使用位结构体的过程中,是否深入思考一下它的相关属性?是否真正用到它的便利性,来提高系统效率?

  开始以为:reserved_1和SYMBOL_TYPE不在一个地址上,因为他们5+4共9位,超过了1个字节地址,但实际他们共用首地址了;而且reserved_2只定义了8位,竟然实际占用了4个字节(0x1fff0834 - 0x1fff0830),我本来是想让他占用1个字节的。WORDS整体占了8个字节(0x1fff0834 - 0x1fff082c),设计时分析占用5个字节

  同理,上面的5个变量,共用一个地址就不足为奇了。而且有效位的分配不是连续进行的,例如SYMBOL_TYPE+reserved_1 共9位,超过了一个字节,索性系统就分配两个字节给他们,每人一个;SYMBOL_NUMBER+SYMBOL_ACTIVE 共8位,一个字节就能搞定。

  当换成uint_8后,可以看到地址空间占用大大减小,reserved_2只占用1个字节(0x1fff069f - 0x1fff069e),其他变量也都符合上面的结论猜想。但是,注意看上面黄色和红色的语句,总感觉有些勉强,那么我又会想,前两个变量数据域是9位,那么他们实际上是不是真正的独立呢?虽然在uint_8上面他们是不同的地址,在uint_32的时候是不是也是不同的地址空间呢?  3、分析结构体内部的数据域是否连续,看下图及结果

  本来假设: 由前2次试验的结论,一共占用8个字节,节空间占用:(2+4)+(4+4)+(2+2+4)+(2+2)+(6)。可是,实际效果并不是想的那样。实际只占用了4个字节,系统并没有按照预想的方式,为RESERVED变量分配4个字节。

  这些数据域,整体相加一共32位,占用4个字节(不考虑数据对齐问题)。而实际确实是占用了4个字节,唯一的原因就是:这些数据域以紧凑的方式链接,没有任何空闲位。实际是不是这样呢?

  这里为了验证是否紧凑链接,用到了一个union数据,后面会讲到用union不会对数据组织方式有任何影响,看实际与上次的一样,也能分析出来。

  主要是分析第2和第3个数据域是否紧密链接的。OBJECT_ACTIVE_PRE赋值0b00001111,NUMBER_ACTIVE赋值0b00000101,其他变量都是0,看到WORD数值0b00。分析WORD数据,可以看到这款MCU还是小端格式(高位数据在高端,低位数据在低端,这里不对大小端进行讨论),断开数据变成(0)10111 11000000,正好是0101+1111,OBJECT_ACTIVE_PRE数据域,跨越了两个字节,并不是刚开始设想的那样。这就印证了上面的紧密链接的结论,也符合数据结果输出。  4、再次实验,分析数据是否紧密链接,看下图和结果

  可以看到,RESERVED数据域已经不再属于4个地址空间内了(0x1fff0518 - 0x1fff051b),但是他们整体加起来还是32个位域。这说明数据中间肯定有“空隙”存在了,空隙在哪?看一下NUMBER_STATE,如果紧密的话它应该跟NUMBER_ACTIVE在同一个字节地址上,可是他们并不在一块,“空隙”就存在这里。

  这两个结构体有什么不一样?数据类型不一致,一个是uint_32,一个是uint_8。综上所述:数据类型影响的是编译器在分配物理空间时的大小单位,uint_32是以4个字节为单位,而后面的位域则是指在已经分配好的物理空间内部再紧凑的方式分配数据位,当物理空间不能满足位域时,那么系统就再次以一定大小单位进行物理空间分配,这个单位就是上面提到的uint_8或者uint_32。

  举例:上面uint_32时,这些位域不管是不是在一个字节地址上,如果能够紧凑的分配在一个4字节空间大小上,就直接紧凑分配。如果不能则继续分配(总空间超过4字节),则再次以4字节空间分配,并把新的位域建立在新的地址空间上(条目1上的就是)。当uint_8时,很明显如果位域不能紧凑的放在一个字节空间上,那么就从新分配新的1字节空间大小,道理是一样的。  5、结构体组合、共用体组合是否影响上述结论

  可以看到,系统并没有因为位结构体上面有uint_4的4字节变量或者共用体类型,就改变分配策略把位域都挤到4字节之内,看来他们是没有什么实质性联系的。这里把uint_32改成uint_8,或者把位结构体也替换掉,经我试验证明,都是没有任何影响的。  总结:

  1、在操作位结构体时,要关注变量的位域是否在一个变量类型(uint_32或者uint_8)上,判断占用空间大小

  2、除了位域,还要关注变量定义类型,因为编译器空间分配始终是按类型分配的,位域只是指出了有效位(小于类型占用空间),而且如果位域大于类型空间,编译器直接报错(如 uint_8  test  :15,可自行实验)。

  3、这两个因素都影响变量占用空间大小,具体可以结合调试窗口,通过地址分配分析判断

  4、最重要的一点:上面的所有结果,都是基于我自己的CodeWarrior10.2和MQX3.8分析出来的,不同的编译环境和操作系统,都可能会有不同的结果;而且即便是环境相同,编译器的配置和优化选项都有可能影响系统处理结果。结论并不重要,主要想告诉大家这一块隐藏陷阱,在以后处理类似问题时,要注意分析避让并掌握方法。

  文章出处:【微信号:mcu168,微信公众号:玩转单片机】欢迎添加关注!文章转载请注明出处。

  本书的前半部分从抽象数据类型的角度讨论各种基本类型的数据结构及其应用;后半部分主要讨论查找和排序的各....

  本文档的主要内容详细介绍的是单片机C语言程序设计实训100例的程序和电路图合集包括了:基础程序设计,....

  各路大牛的C语言编程建议和技巧,看完感触颇深 我们鼓励在编程时应有清晰的哲学思维,而不是给予硬性规则。我并不希望你们能认...

  《C语言接口与实现:创建可重用软件的技术》概念清晰、实例详尽,是一本有关设计、实现和有效使用C语言库....

  1、实验原理规范要求:给出问题的求解原理,需要详细描述解决当前类型问题的原理、方法及步骤。 2、实验....

  [postbg]bg7.png[/postbg] C语言常用的运算符有哪些并简述其功能? 温馨提示: 请从以下链接中的帖子学习相关...

  [postbg]bg7.png[/postbg]什么是结构体,如何定义结构体? 温馨提示: 请从以下链接中的帖子学习相关内容并进行回答...

  这一层是我自己安排的位置。相比前面几层而言,我觉得它更为重要。写单片机程序,什么最痛苦,当然是做界面....

  linux驱动程序设计本质是属于linux内核编程范畴的,因而是对linux内核和内核编程是有要求的....

  全局/静态存储区:全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和....

  C语言中几个容易踩的“坑”! 今天给大家分享几个C语言中的坑。一、带参数的宏展开顺序#include #define f(a,b) a##b #def...

  今天笔者就带大家跟c语言简单“相个亲”,看看朋友们对c语言的眼缘如何。今天你不需要理解它是什么意思,....

  现在大家只需要学习一下基本类型。其他三种类型更适合在后续分享相关知识点时详细介绍。

  我曾跟大家讲过,为了使零基础的朋友们能够入门,我们一点一滴地去讲c语言的每一个知识点,咱们每天进步一....

  编写程序过程中,名称(name)可以是符号常量、变量、函数、结构、枚举、类和对象等等。工程越大,名称....

  我曾经对朋友们说过,我发表的每一篇文章,都会讲解一个c语言的知识点,让大家循序渐进地学习并掌握c这门....

  看到本文的朋友们,如果你想系统地学习一下c语言程序设计,请您点击文章左上角带“C语言”三个字的蓝色圆....

  在汇编语言中有直接对位进行操作的指令,如置位、复位、位取反、测试某一位等,这对于硬件操作十分方便,在....

  EEPROM为ATMEL公司的AT24C01A。单片机为ATMEL公司的AT89C51。

  2020年了,不要再看网上那些老旧的文章还在教你使用手工生成 tags 的,请使用自动代码索引生成工....

  你们好,我是学习STM32F10X的新手,在读使用C语言编程的驱动初始化时,老是碰到一些小问题。 请求各位高手的...

  学完c语言两年了,看了例程里面的程序,发现一些符号类的都没弄懂。盼解答。u8 t; 语句中u8应该是数据类型,但是貌似c语言里没有...

  一般的机械式按键的构造是两个金属片和一个复位弹簧,按键按下时,两个金属片便被压在了一起。

  从软件工程的角度,我们在做软件开发时,都会强调高内聚、低耦合的原则。而裸机的模块化开发难度非常大,模....

  通过指针,非常方便操作数组。比如串口的接收缓存,将接收缓存的首地址赋给指针后,CPU就可以通过结构体....

  本文档的主要内容详细介绍的是超声波测距串口显示的C语言和工程文件免费下载。

  Windows等操作系统与嵌入式系统中常用的RTOS的主要区别,在于对外部事件的响应时间。Windo....

  这10大C语言基础算法,在面试中会经常遇到! 算法是一个程序和软件的灵魂,作为一名优秀的程序员,只有对一些基...

  当然STM32MP1推出之前, ST公司在MPU市场是缺位的. 之前最高性能的H7系列可以跑到400....

  C语言运算符优先级(超详细) 当想找哪个运算符优先级高时,很多时候总是想找的就没有,真让人气愤!现在,终于有个我个人觉得非...

  51系列单片机支持三种高级语言,即PL/M,C和BASIC。C语言是一种通用的程序设计语言,其代码率....

  Google 不久前在官方博客上宣布了开源 Pigweed 的消息。Pigweed 是啥?一个月前,....

  本文档的主要内容详细介绍的是51单片机C语言程序100实例的详细资料说明包括了:用定时器 T0查询方....

  本书共分成三大部分。第一部分是应用研究篇,介绍8051单片机的诸多设计理念与硬件保护手段。第二部分是....

  冯·诺依曼结构也称普林斯顿结构,是一种将程序指令存储器和数据存储器合并在一起的存储器结构。程序指令存....

  本文档的主要内容详细介绍的是Jacobi迭代求解特征值和特征向量的C语言代码免费下载。

  本系统采用微处理器(单片机89C52、205I)实现小汽车的门功往返井达到时间的精确控制、起始位置的....

  《51单片机C语言常用模块与综合系统设计实例精讲》全书针对目前最通用流行的51单片机系列,通过大量典....

  许多初学者在编程入门之前,都会在编程语言的选择上犹豫不决。一般来讲,Java和C语言是编程小白最青睐....

  不管什么样的编程语言,数据类型的不断衍生都是为了不同场合对其进行不同处理或管理。 比如单一的变量,我....

  首先说明:写这个第一个Linux设备驱动程序的目的是熟悉Linux驱动的框架以及编程流程,所以是通过....在电力、石油、化工和锅炉等行业中的焊接生产中,全位置管道自动焊接机是一种重要的专用焊接设备,研制该设....

  早期的MATLAB是用Fortran语言编写的,只能作矩阵运算,绘图也只能用极其原始的方法,内部函数....

  本文档的主要内容详细介绍的是使用单片机设计实现45例实验的仿真图和C语言程免费下载。

  对于计算机语言的发展史,业界一般认为:B语言导致了C语言的诞生,C语言演变出了C++语言,而C++语....

  用可调电阻调节电压值作为模拟温度的输入量,当温度低于30℃时,发出长嘀报警声和光报警,当温度高于60....

  过去五年中,移动应用程序开发行业实现了跨越式增长,改变了全球业务运作方式。随着企业最近使移动应用程序....初学C语言的朋友应该首先了解C语言关键的核心概念(结构化、三个执行流程、优先级、指针、文件、共用体、....

  概述 系统通过SHT11温湿度传感器感应周围的环境的温度和湿度,通过单片机对采集到的数据进行读取处理....

  随着电子工业的发展,具有语音控制功能的小车越来越受到人们的青睐,在人们的日常消费生活中起着不可忽视的....

  《C Primer Plus(第6版)中文版》共17章。第1、2章介绍了C语言编程的预备知识。第3~....

  《C指针编程之道》由孔浩、张华杰、陈猛编著,是一本帮助程序员提高编程素养的图书,书中结合开发人员多年....

  我是刚入门学STM32单片机的,在这个过程中我遇到了一些困难,就是在使用keil5编程 的时候看不懂....

  在本书的开篇,我们首先概要地介绍C语言,主要是通过实际的程序引入C语言的基本元素,至于其中的具体细....