14.C基础_结构体

定义与使用

1、定义

定义结构体:

定义结构体时,需要注意最后的分号必须加上。

定义结构体时,成员只去声明类型,不进行赋值。赋值在定义结构体变量时进行。

struct 结构体名{
    结构体成员列表
};                //注意这里的分号

定义结构体变量:

方法1:正常定义结构体变量

struct 结构体名 结构体变量名;

方法2:定义结构体时,定义结构体变量

struct 结构体名{
    结构体成员列表
}变量名1,变量名2; //直接在这里写变量名,变量名之间用,隔开               

2、初始化结构体变量

情况1:全部成员赋值

全部成员赋值就是在定义结构体变量后直接加上成员值,各个成员值之间用" , "分隔。

struct 结构体名 结构体变量名 = {依次赋值的成员值};

情况2:部分成员赋值

部分成员赋值需要在赋值时指定成员并赋值。

struct 结构体名 结构体变量名 = {
    .成员1 = 值,
    .成员2 = 值
};

情况3:定义结构体时,定义结构体变量的初始化

struct 结构体名{
    结构体成员列表
}变量名 = {成员值};

3、访问结构体变量

方式1:当为结构体变量时,用" . "来访问成员

方式2:当为结构体指针时,用" -> "来访问成员

结构体数组

结构体数组就是一个数组,它的成员是一个结构体变量。

定义与赋值:

定义与赋值的方法与一维数组完全一样。

struct 结构体名 数组名[数组大小] = {{第1个结构体成员初始化值},{第2个结构体成员初始化值}};

结构体嵌套结构体

结构体嵌套结构体就是结构体中成员的类型可以是结构体。

作用:

解决不同结构体中重复的、完全一样的部分

示例:

假设现在要创建两个结构体来管理学生、老师的信息。对于学生,有姓名、性别、学分这三个信息;对于老师,有姓名、性别、电话号码这三个信息。可以看到,在学生和老师的信息中姓名、性别这两个信息是重复的。因此可以建立一个结构体来封装它们,这样使得学生和老师的结构体定义变得更加简洁。

代码如下:

#include <stdio.h>
struct both{   //老师和学生共有的成员
	char* name;
	char* sex;
};
struct student{//学生信息结构体
	struct both stu;
	int score;
};
struct teacher{//老师信息结构体
	struct both tea;
	char* phone;
};

int main(){

	//struct both tBothStu = {"stuName","沃尔玛购物袋"};
	struct both tBothTea = {"teaName","纸扎投石车"};

	struct student stu1 = {{"stuName","沃尔玛购物袋"},100};//可以直接赋值结构体
	struct teacher tea1 = {tBothTea,"123124123"};//也可以通过定义的结构体进行初始化

	printf("stu1:name=%s  sex=%s  score=%d\n",\
			stu1.stu.name,\
			stu1.stu.sex,\
			stu1.score
		  );
	printf("tea1:name=%s  sex=%s  phone=%s\n",\
			tea1.tea.name,\
			tea1.tea.sex,\
			tea1.phone
		  );
	return 0;
}

运行结果如下: 

结构体的大小

字节对齐是什么:

对于结构体,编译器会自动对其成员变量进行对齐,以提高数据存取的效率。

结构体大小计算步骤:

相关名词:

  • 自身对齐:数据类型的大小就是自身对齐的大小,例如char为1,int为4
  • 默认对齐:与电脑位数有关。32位默认对齐为4,64位默认对齐为8
  • 有效对齐:有效对齐=min(自身对齐,默认对齐),是结构体中实际占用空间的大小
  • 存放地址:存放地址一定能够整除有效地址

计算步骤:(依次计算成员空间)

  1. 确定自己操作系统的位数,从而确定默认对齐方式
  2. 计算第一个成员,通过公式:" 有效对齐 = min(自身对齐,默认对齐) "计算有效对齐
  3. 查看当前的存放地址是否能够整除有效对齐字节数,不能整除需要补空间对齐。                    注意:补其后能够整除的数值是下一个变量的起始地址,并没有存储数据                            例如:补其后是12,那么实际的有数据的空间只到11                  
  4. 依次计算其他成员空间
  5. 查看最终存放地址是否能够整除" max(全部有效对齐) "

结构体大小手算示例:

假设有一个如下的结构体,在64位的操作系统下计算其结构体大小(假设起始地址为0x00)

struct test{
    char a;
    int b;
    char c;
    char d;
};

 列出如下表格,依次计算存储地址:

变量名类型自身对齐默认对齐有效对齐存储地址
achar18(64位)10 1 2 3
bint48(64位)44 5 6 7
cchar18(64位)18
dchar18(64位)19 10 11

计算a:可以计算出有效对齐为1,存储地址为0,可以整除,所以存储地址为0

计算b:可以计算出有效对齐为4,存储地址为1,不能整除,所以补齐到最小的可以整除4的地址,即:补齐到4。a的空间为0,1,2,3;b的空间为4,5,6,7这四个字节用于存储

计算c:可以计算出有效对齐为1,存储地址为8,可以整除,所以存储地址为8

计算d:可以计算出有效对齐为1,存储地址为9,可以整除,所以存储地址为9

整体大小计算:可以计算出max(全部有效对齐)=max(1,4,1,4)=4,存储地址9不能整除4,所以补齐到最小的可以整除4的地址,即:补齐到12。因此最终结构体大小为0~11,共12字节

位域

什么是位域:

位域就是把一个字节中的二进制位分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。

例如:现在有一个字节的空间,可以让bit0~bit2表示信息a,让bit3~bit5表示信息b,让bit6~bit7表示信息c。这样一个字节分为了3个域,域名就是a,b,c;a域的位数为3,b域的位数为3,c域的位数为2。

位域的定义:

位域定义就是把结构体中的成员列表换成位域列表,格式为:<类型> <位域名>:<位域长度>

以例题为例,那么a,b,c这三个域的定义如下:

struct test{
    char a:3;
    char b:3;
    char c:2;
};

对于上述定义,如果按照结构体成员来定义,计算出的大小应该为3字节。而按位域方法定义,计算出来的大小为1字节。这样就节省了空间。

补充练习:结构体大小手算例题 

例题1:

假设有一个如下的结构体,在64位的操作系统下计算其结构体大小(假设起始地址为0x00)

struct test{
    char a;
    short b;
    char c;
    char d;
};

 列出如下表格,依次计算存储地址:

变量名类型自身对齐默认对齐有效对齐存储地址
achar18(64位)10 1
bshort28(64位)22 3
cchar18(64位)14
dchar18(64位)15

 最终补齐地址为6,实际存储空间为0~5,6字节

例题2:

假设有一个如下的结构体,在64位的操作系统下计算其结构体大小(假设起始地址为0x00)

struct test{
    char a;
    double b;
    char c;
    char d;
};

 列出如下表格,依次计算存储地址:

变量名类型自身对齐默认对齐有效对齐存储地址
achar18(64位)10 1 2 3 4 5 6 7
bdouble88(64位)88 9 10 11 12 13 14 15
cchar18(64位)116
dchar18(64位)117 18 19 20 21 22 23

 最终补齐地址为24,实际存储空间为0~23,24字节

内容概要:本文介绍了一个基于冠豪猪优化算法(CPO)的无人机三维路径规划项目,利用Python实现了在复杂三维环境中为无人机规划安全、高效、低能耗飞行路径的完整解决方案。项目涵盖空间环境建模、无人机动力学约束、路径编码、多目标代价函数设计以及CPO算法的核心实现。通过体素网格建模、动态障碍物处理、路径平滑技术和多约束融合机制,系统能够在高维、密集障碍环境下快速搜索出满足飞行可行性、安全性与能效最优的路径,并支持在线重规划以适应动态环境变化。文中还提供了关键模块的代码示例,包括环境建模、路径评估和CPO优化流程。; 适合人群:具备一定Python编程基础和优化算法基础知识,从事无人机、智能机器人、路径规划或智能优化算法研究的相关科研人员与工程技术人员,尤其适合研究生及有一定工作经验的研发工程师。; 使用场景及目标:①应用于复杂三维环境下的无人机自主导航与避障;②研究智能优化算法(如CPO)在路径规划中的实际部署与性能优化;③实现多目标(路径最短、能耗最低、安全性最高)耦合条件下的工程化路径求解;④构建可扩展的智能无人系统决策框架。; 阅读建议:建议结合文中模型架构与代码示例进行实践运行,重点关注目标函数设计、CPO算法改进策略与约束处理机制,宜在仿真环境中测试不同场景以深入理解算法行为与系统鲁棒性。
<think>嗯,用户想将Python的ctypes结构体RKLLMParam转换为Node.js代码。首先,我需要回忆一下Python中的ctypes结构体是如何定义的,然后考虑Node.js中有哪些库可以实现类似的功能。之前用户提到过node-ffi,但最新的信息可能推荐使用ffi-napi或者ref-struct这样的库。 首先,结构体的每个字段都需要对应到Node.js中的类型。比如,c_char_p对应到string或者pointer,c_int32对应到int,c_float对应到float,c_bool对应到bool,还有结构体中的extend_param字段是另一个结构体RKLLMExtendParam,这意味着需要先定义这个子结构体,然后再在主结构体中引用。 接下来,我需要考虑Node.js中如何处理结构体。在Python中使用ctypes,而在Node.js中通常使用ref-struct库来定义结构体。不过用户之前的问题是关于node-ffi的,可能需要结合ffi-napi和ref-struct来使用。需要确保这些库的安装和兼容性,尤其是不同平台下的情况。 然后,检查每个字段的类型转换是否正确。比如,img_start、img_end、img_content都是c_char_p,在Node中可能需要用string类型或者使用Buffer来处理,特别是如果这些字段指向二进制数据的话。不过根据之前的例子,用户可能更倾向于使用string,因为如果是普通字符串的话。但如果有二进制内容,可能需要用pointer或者Buffer类型。 另外,布尔类型c_bool在Node中需要转换为bool,但要注意C语言中的布尔通常是1字节,而JavaScript的布尔值可能需要转换,可能需要使用ref库中的bool类型,或者用1和0来表示。 还有,extend_param是一个RKLLMExtendParam结构体,所以需要先定义这个结构体,然后在主结构体中引用。这需要确保在Node代码中先定义子结构体,再定义主结构体。 然后,考虑内存对齐的问题。ctypes的结构体可能会有不同的内存对齐方式,而Node.js的ref-struct库需要指定正确的打包方式,尤其是在跨平台时。可能需要设置pack属性来确保内存布局一致。 用户的需求可能是在Node.js中调用一个使用RKLLMParam结构体的本地库,所以结构体的定义必须与C/C++端的定义完全一致,否则会导致内存错误或数据解析错误。因此,必须确保每个字段的类型、顺序和大小都匹配。 此外,用户可能需要使用ffi-napi库来定义外部函数,这些函数接受或返回这个结构体。需要注意,当传递结构体时,通常是通过指针传递的,所以可能需要使用ref.types.CStruct来引用结构体类型,或者在调用库函数时传递结构体的指针。 最后,可能需要测试这个结构体是否正确,比如创建一个实例,填充各个字段,然后传递给本地函数,看看是否正常工作。如果出现内存错误或数据错误,需要检查结构体定义是否正确,以及字段类型是否匹配。 总结一下,步骤应该是: 1. 安装必要的Node库:ffi-napi、ref、ref-struct、ref-array等。 2. 定义RKLLMExtendParam结构体(如果存在的话)。 3. 定义RKLLMParam结构体,确保字段类型和顺序正确。 4. 在调用外部函数时,正确传递结构体实例的指针。 5. 处理可能的内存管理和类型转换问题。 需要注意的潜在问题包括类型大小不一致、内存对齐差异、字符串编码问题(比如C中的char*是UTF-8还是其他编码),以及布尔值的表示方式。此外,如果结构体中有动态分配的内存,需要确保正确管理生命周期,避免内存泄漏。</think>以下是逐步将`RKLLMParam`结构体从Python ctypes转换为Node.js的实现过程: --- ### **步骤1:安装依赖库** ```bash npm install ffi-napi ref-napi ref-struct-di ``` --- ### **步骤2:基础类型映射** ```javascript const { types } = require('ref-napi'); const Struct = require('ref-struct-di')(types); ``` --- ### **步骤3:定义子结构体** (假设`RKLLMExtendParam`存在) ```javascript // 先定义子结构体 const RKLLMExtendParam = Struct({ param1: types.int32, param2: types.CString }); ``` --- ### **步骤4:主结构体定义** ```javascript const RKLLMParam = Struct({ model_path: types.CString, // c_char_p → CString max_context_len: types.int32, // c_int32 → int32 max_new_tokens: types.int32, top_k: types.int32, top_p: types.float, // c_float → float temperature: types.float, repeat_penalty: types.float, frequency_penalty: types.float, presence_penalty: types.float, mirostat: types.int32, mirostat_tau: types.float, mirostat_eta: types.float, skip_special_token: types.uchar, // c_bool → uchar(1字节) is_async: types.uchar, // 同上 img_start: types.CString, img_end: types.CString, img_content: types.CString, extend_param: RKLLMExtendParam // 嵌套结构体 }); ``` --- ### **步骤5:内存对齐配置** ```javascript // 显式设置结构体对齐方式(需与C端一致) RKLLMParam.defineAlignment(4); // 4字节对齐 RKLLMExtendParam.defineAlignment(4); ``` --- ### **步骤6:创建实例** ```javascript const param = new RKLLMParam(); param.model_path = Buffer.from('/path/to/model.bin\0'); // 必须显式添加NULL终止符 param.max_context_len = 4096; param.skip_special_token = 1; // true → 1 param.is_async = 0; // false → 0 ``` --- ### **步骤7:与FFI结合使用** ```javascript const ffi = require('ffi-napi'); const lib = ffi.Library('yourlib', { 'llm_init': [ 'pointer', [ RKLLMParam ] ] // 传递结构体指针 }); const paramPtr = param.ref(); const handle = lib.llm_init(paramPtr); ``` --- ### **关键注意事项** 1. **字符串处理**: ```javascript // 正确方式:显式分配内存 const strBuffer = Buffer.alloc(256); // 预分配内存 strBuffer.write('image_content', 0); param.img_content = strBuffer; ``` 2. **布尔值处理**: ```javascript // 使用1/0代替true/false param.skip_special_token = process.env.SKIP_TOKENS ? 1 : 0; ``` 3. **嵌套结构体初始化**: ```javascript const extendParam = new RKLLMExtendParam(); extendParam.param1 = 100; param.extend_param = extendParam; ``` 4. **内存泄漏防护**: ```javascript // 显式释放内存 const destroyParam = lib['llm_free_param']; destroyParam(paramPtr); ``` --- ### **完整类型对照表** | C类型 | Node.js类型 | 特殊处理 | |--------------------|-----------------------|---------| | c_char_p | `Buffer`/`types.CString` | 需要NULL终止 | | c_int32 | `types.int32` | 直接赋值 | | c_float | `types.float` | 自动转换 | | c_bool | `types.uchar` | 1/0代替 | | 嵌套结构体 | `Struct`实例 | 预初始化 | --- ### **调试建议** 1. 使用`hexdump`验证内存布局: ```javascript console.log(param.ref().hexAddress()); // 查看内存地址 ``` 2. 检查结构体大小是否匹配: ```javascript console.log('Node结构体大小:', RKLLMParam.size); // 应与C端的sizeof(RKLLMParam)一致 ``` 通过以上步骤可实现精确的结构体转换,确保与原生库的二进制兼容性。实际使用时需结合具体ABI文档验证内存布局。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值