自定义类型---结构体

1.结构体类型的声明

<1>结构体类型的声明

                      

如图中所示就是结构体变量声明的例子,在其中,必须要加上struct这个结构体关键字,struct后面则是加上结构体类型名,大括号内的内容是成员变量,在末尾有一个分号,我们可以在大括号和分号之间进行结构体类型的创建也可以在主函数内创建(两种方式所不同的差别是变量的作用域不同,一个全局一个局部)

在引用结构体变量的成员时需要用到.操作符或者->操作符

<2>匿名结构体类型

  匿名结构体类型就是在struct后面不加上结构体类型名。

这样的操作是在你只想对这个类型使用一次,以后再也不使用了的情况下使用

                                             

对于上述代码,如果你对它使用p=&a这样的操作后,编译器会报警告,认为不能这样操作,这是因为两个结构体都没有具体的名字(两个结构体虽然成员类型相同,但是没有名字,编译器就会认为这两个是不一样的)。

<3>结构体的自引用

-->问题1:

   在结构体中包含一个类型为结构体本身的成员是否可以呢?

   用结构体模拟一个链表来举例:

                     

  我们会发现一个问题,我的Node next中包含了无数个date和Node next,那么这个结构体的内存    大小是多少呢,我们并不知道,所以这样的自引用方式肯定是不对的。

   但是如果把next改成*next就会成功了,即变成一个指针的形式,所以结构体变量里面包含一个       同类型的结构体变量是做不到的。

-->问题2:

   在结构体自引用的过程中,夹杂了ttypedef对匿名结构体类型重命名,也容易出现问题,比如:

                                 

  如果这样书写代码编译器就会在第四行报警告,因为不认识第四行中的Node,这是因为其前面没有加上struct,这是一个顺序问题,是先有了结构体再重命名,而在上面不加struct则是在没有命名之前就使用了命名之后的东西,明显是不对的方式。

2.结构体的内存对齐

    对齐现象的演示:

     在c语言中如何计算结构体的大小呢?举下面这个例子:

                         

    我最初的想法是两个结果都是6,但是并不是这样的,那是为什么呢。

    那是因为结构体的成员在内存中是存在对齐现象的。

        为了解释上述现象,我们可以对其进行验证,这是我们需要用到一些没有学习过的知识即: offsetof,这在C语言中是一个宏的定义。

我们可以在cpulspuls官网中查询到他的具体功能如下:

对于offsetof,它可以计算结构体成员相较于结构体变量起始位置的偏移量。

那么什么东西是偏移量呢,在内存的开始位置往后每一个内存单元,就相当于一个偏移量,类比为很多个格子,但是最开始的那个内存单元所对应的偏移量是0而不是1,依次往下。

                            

   以上是对offsetof的应用,再对照上述程序的结果,我们可以由此可以得到以下的示意图(最上面的红线我们把它当作结构体变量内存的开始):

                            

然后我们会发现,其中2,3所对应的内存单元是空的,所以在最开始的时候我们可以看到那个8个字节的结果。同理也可以验证struct s2的情况。了解了对齐的规则,接下来我们来了解一下,对齐的具体内容。

<1>对齐规则

在第二条中,所谓的整数倍的地址处,其实就是找偏移量为整数倍的地方,等一下再举例来解释,结构体的总大小取决于第三点。

--->下面举例一下:

                         

      按照上面规则来分析:

                         

   如何解释上面这张图呢,第一个成员为double,有8个字节,所以从偏移数为0的位置开始,如何在是第二个成员,char字节为1如果默认对齐数是8(当前为vs环境),所以对齐数是1,1的倍数是8,就接着上一个成员开始,然后到最后一个成员,由规则看可以知道,对齐数是4,如何找作为4的倍数的偏移位就是12,如何开始往下四个字节。显然的是中间的8,10,11偏移位这三个字节的大小被我们浪费掉了。

   在所有的对齐数中,最大的是double的对齐数为8,所以最终结果为8的倍数,所以上述图中的16个字节就是最终答案。

--->现在来举例一下第四种情况:

                      

     下面根据规则4来解释为什么struct s2的大小是32个字节:

                                                              

如图所示,char成员先占据偏移量为0的那个字节,然后到第二个成员,也就是嵌套的那个结构体类型,它需要对齐到自身最大对齐数的整数倍数处,易得其最大对齐数为8,所以这个结构体变量从偏移量为8的位置开始,然后一直向下占据16个字节,最后一个成员double的位置很容易就可以得到,最后是这个结构体类型占据多少字节,它是所有最大对齐数的整数倍数,所以32是正确的,即s2就占据32个字节的大小。

<2>为什么存在内存对齐

        以下是大部分参考书的说法:

如果存在对齐的话,那么访问效率会大大提升,所以内存的对齐是十分有必要的。

在设计结构体时,我们既要要求对齐,又要节省空间,我们可以:起让占用空间小的成员尽量放在一起,这样就可以更有效(并不是等同于小的必须要放在前面)。

<3>修改默认对齐数

        在c语言中修改默认对齐数需要使用#pragma pack(num)这个语句来修改默认对齐数。其中的num就是你先要的默认对齐数。注意:一般我们设置默认对齐数也有一定的技巧,一般都设为2的n次方,当然这个n也包含了0在内

3.结构体传参

     和平常的自定义函数类似,在传递的参数是结构体类型的变量时,我们有传值调用和传址调用。

     但是在这种前提下,如果我们采用传值调用的方式对结构体变量进行传递,会新创建一个结构体变量,从而占据大量空间(特别是遇到结构体内存较大的情况下)。那么我们会有以下的两个总结。

    <1>函数传参的时候,参数是需要压栈的,所以会有时间和空间上的系统开销。

    <2>如果传递一个结构体对象的时候结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。

4.结构体实现位段

   <1>什么是位段:

            位段的声明和结构体是类似的,有两点不同:

           1.位段的成员必须是int,unsigned int,signed int,在c99中位段成员的类型也可以选择其他的类型。

           2.位段的成员后面必须有一个冒号和一个数字。

              举例如下:

                                 

            这个代码是什么意思呢,首先要了解位段中的位是比特位的意思,那么这个代码就是让这个int_a的大小只占2个比特位,其余类推。明显的,这是一种节省空间的方法。

   <2>位段的内存分配

            1. 位段的成员可以是int, unsigned int,signed int 或者是char等类型。

            2.位段的空间是按照需要以4个字节或者1个字节来开辟空间的。

   <3>位段的跨平台问题

总结:跟结构体相比位段可以达到同样的效果,也可以很好的节省空间,但是有跨平台的问题存在。

   <4>位段的应用

   <5>位段使用的注意事项

基于数据驱动的 Koopman 算子的递归神经网络模型线性化,用于纳米定位系统的预测控制研究(Matlab代码实现)内容概要:本文围绕“基于数据驱动的Koopman算子的递归神经网络模型线性化”展开,旨在研究纳米定位系统的预测控制方法。通过结合数据驱动技术与Koopman算子理论,将非线性系统动态近似为高维线性系统,进而利用递归神经网络(RNN)建模并实现系统行为的精确预测。文中详细阐述了模型构建流程、线性化策略及在预测控制中的集成应用,并提供了完整的Matlab代码实现,便于科研人员复现实验、优化算法并拓展至其他精密控制系统。该方法有效提升了纳米级定位系统的控制精度与动态响应性能。; 适合人群:具备自动控制、机器学习或信号处理背景,熟悉Matlab编程,从事精密仪器控制、智能制造或先进控制算法研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①实现非线性动态系统的数据驱动线性化建模;②提升纳米定位平台的轨迹跟踪与预测控制性能;③为高精度控制系统提供可复现的Koopman-RNN融合解决方案; 阅读建议:建议结合Matlab代码逐段理解算法实现细节,重点关注Koopman观测矩阵构造、RNN训练流程与模型预测控制器(MPC)的集成方式,鼓励在实际硬件平台上验证并调整参数以适应具体应用场景。
<think>我们正在讨论CAPL语言中的数据类型,特别是结构体(struct)的使用方法。根据引用内容,CAPL的数据类型包括基本类型、结构体、枚举、关联类型和对象类型。结构体是一种用户自定义的数据类型,它允许将多个不同类型的数据组合成一个单一的类型。 在CAPL中,结构体的定义与C语言类似,使用关键字`struct`。例如,我们可以定义一个表示点的结构体: ```c struct Point { int x; int y; }; ``` 然后可以声明该结构体类型的变量,并通过点运算符访问其成员: ```c struct Point p1; p1.x = 10; p1.y = 20; ``` 引用[4]给出了一个使用结构体组装DoIP报文的例子。它定义了两个结构体:一个用于表示整个DoIP报文(DoIPMessage),另一个用于表示车辆声明消息的特定头部(VehicleAnnouncement)。VehicleAnnouncement结构体被用作DoIPMessage结构体的Payload部分。 下面是一个简化的示例,展示如何用结构体组装一条DoIP车辆声明消息: ```c // 定义车辆声明消息的结构 struct VehicleAnnouncement { byte protocolVersion; // 协议版本 byte inverseProtocolVersion; // 协议版本的反码 word logicalAddress; // 逻辑地址 byte VIN[17]; // 车辆识别号 // 其他字段... }; // 定义DoIP报文结构 struct DoIPMessage { byte version; // DoIP首部版本 byte inverseVersion; // 版本的反码 word payloadType; // 载荷类型 dword payloadLength; // 载荷长度 struct VehicleAnnouncement payload; // 载荷部分,这里就是车辆声明消息 }; // 使用示例 struct DoIPMessage doipMsg; doipMsg.version = 0x02; doipMsg.inverseVersion = 0xFD; // 0x02取反(按位取反)实际上是0xFD,但注意:这里要求是反码,即每个位取反,但通常用0xFF减去原值 doipMsg.payloadType = 0x0004; // 车辆声明消息的类型 doipMsg.payloadLength = sizeof(doipMsg.payload); // 计算载荷长度 // 填充车辆声明消息 doipMsg.payload.protocolVersion = 0x01; doipMsg.payload.inverseProtocolVersion = 0xFE; doipMsg.payload.logicalAddress = 0x0E80; // 例如,0x0E80表示测试设备 memcpy(doipMsg.payload.VIN, "12345678901234567", 17); // 复制VIN字符串 ``` 注意:上面的代码中,`word`和`dword`分别表示16位和32位的无符号整数。在CAPL中,可以使用`word`和`dword`类型。 另外,在发送UDP报文时,可以将整个结构体作为数据发送。但是需要注意结构体的内存对齐问题。CAPL中结构体的内存布局与C语言相同,通常按照成员的定义顺序排列,并且可能会进行字节对齐(可以通过`#pragma pack`指令调整)。为了确保结构体在网络上传输的正确性,需要确保发送方和接收方对结构体的解释一致。 在CAPL中发送UDP报文,可以使用`udpSend`函数,将结构体转换为字节数组。例如: ```c byte buffer[sizeof(doipMsg)]; memcpy(buffer, &doipMsg, sizeof(doipMsg)); udpSend(buffer); ``` 或者直接传递结构体变量(因为CAPL在调用`udpSend`时,会自动将结构体转换为字节序列): ```c udpSend(doipMsg); ``` 但更推荐显式复制到字节数组,因为这样可以更好地控制。 总结一下,在CAPL中使用结构体: 1. 使用`struct`关键字定义结构体类型。 2. 声明结构体变量,并通过点运算符访问成员。 3. 结构体可以嵌套使用(如示例中的DoIPMessage包含VehicleAnnouncement)。 4. 注意结构体的内存布局,确保网络传输时的一致性(考虑字节序和字节对齐)。 5. 可以使用`sizeof`运算符获取结构体的大小。 希望这些信息能帮助你理解CAPL中结构体的使用方法。 §§相关问题§§ 1. CAPL中结构体的内存对齐规则是什么?如何调整? 2. 除了结构体,CAPL中还有哪些复合数据类型? 3. 在CAPL中如何将结构体转换为字节数组进行网络传输? 4. 结构体在CAPL中可以作为函数的参数和返回值吗?</think>### CAPL语言中的结构体数据类型及使用方法 在CAPL编程中,结构体(struct)是一种重要的复合数据类型,用于将多个不同类型的数据组合成一个逻辑单元。以下是详细说明: #### 1. **结构体定义** 使用`struct`关键字定义自定义数据类型,语法类似C语言: ```c struct Point { // 定义二维点结构体 int x; int y; char label[20]; }; ``` #### 2. **结构体变量声明** 定义后即可声明变量: ```c struct Point p1; // 声明结构体变量 struct Point path[10]; // 结构体数组 ``` #### 3. **成员访问** 通过点运算符`.`访问成员: ```c p1.x = 100; p1.y = 200; strncpy(p1.label, "Origin", sizeof(p1.label)); ``` #### 4. **结构体嵌套** 支持多层嵌套(如引用[4]的DoIP示例): ```c struct Vehicle { char VIN[17]; struct Point position; // 嵌套结构体 }; ``` #### 5. **内存操作** 可直接用内存函数处理结构体: ```c byte rawData[sizeof(struct Point)]; memcpy(rawData, &p1, sizeof(p1)); // 结构体转字节数组 ``` #### 6. **实际应用示例** 参考引用[4]的DoIP报文组装: ```c // 定义车辆声明消息 struct VehicleAnnouncement { byte protocolVersion; byte inverseVersion; word logicalAddress; }; // 定义完整DoIP报文 struct DoIPMessage { byte version; byte inverseVersion; word payloadType; dword payloadLength; struct VehicleAnnouncement payload; // 嵌套结构体 }; // 初始化报文 struct DoIPMessage msg; msg.version = 0x02; msg.payload.protocolVersion = 0x01; // 访问嵌套成员 ``` #### 7. **使用注意事项** - **内存对齐**:CAPL默认按1字节对齐(无填充),与C语言不同 - **赋值方式**:支持整体赋值 `struct Point p2 = p1;` - **大小计算**:使用`sizeof(struct Point)`获取内存大小 - **网络传输**:需考虑字节序问题(大端/小端) > 结构体特别适合处理协议报文等复杂数据结构,通过逻辑分组提高代码可读性和可维护性[^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值