rust学习二十.1、不安全代码之原始指针(裸指针)

news/2025/3/10 17:52:40/文章来源:https://www.cnblogs.com/lzfhope/p/18759133

一、前言

指针在前面的篇幅中已经介绍过许多,但主要是智能指针。

智能指针管理堆上的数据,并且受到rust的所有权和借用规则管理(注意,这里的所有权和借用有时候不同于最原始的那种)。

智能指针好歹能管着这些数据,但是rust中存在一些不能使用所有权管理的数据,它们需要利用原始指针来管理。

本文简要讨论原始指针(raw pointer)。

注:<<Rust程序设计语言>>翻译为裸指针,个人觉得不太贴却,那样容易让人联想到没有元数据的智能指针,其次“裸”并没有关联到raw的核心意思:

未经加工的;生的;未经处理的;真实的;原始的;寒冷的;自然状态的;未经训练的;未经分析的;工作生疏的;未烹制的;未煮的;红肿疼痛的;

二、定义

在那本书中,并没有给出原始指针的定义,找了一圈,大体可以如下定义:

  1. 原始指针类似c,c++中的原始指针
  2. 原始指针可以手动、直接操作内存,但是需要手动释放
  3. 原始指针不遵守所谓所有权和借用原则,不保证操作安全性

在代码上,如下定义一个原始指针:

  • *const T  -- 不可变原始指针
  • *mut T    -- 可变原始指针

注意:这里的✳号不是表示解引用,就是和C中的指针前的*差不多的意思。

以下是C语言中的指针定义示例:

int *ptr;

char *str = "Hello, World!";

三、作用

如前定义,原始指针的主要作用是直接操作内存,同时超凡脱俗(不用管所有权和借用原则)的特性导致可以用于以下几种业务场景:

  1. 调用其它语言的函数,目前主要是C语言
  2. 执行低级别的内存操作-所谓低级别,就是不用管rust的一些规则,显得初级生猛
  3. 实现高级数据结构和算法,这里主要指内存、并发数据、硬件接口等一些方面
  4. 优化性能-用于极端环境下的性能优化。总之快是顶级快,只是不保障安全。这是少数环境下的要求
  5. 提供编程灵活性-简而言之就是rust规则之外的补充。 要只知道世界至少有两面,一个面是复合rust规则,一面是不复合的。

毫无疑问,即使使用rust编码,使用到原始指针的机会也不会很多,否则不如用C,C++之类的语言。

四、示例

毫无疑问,现在对于原始指针以及内存操作并不熟悉,本文的例子基本上是来自<<rust程序设计语言>>,只不过部分稍作了一些调整。

此处示例主要关于:原始指针的创建和销毁

use std::ffi::CString;
use std::os::raw::c_char;
use std::slice;fn main() {//1.0 演示原始指针的创建和访问,以及可以同时拥有多个指向相同位置的原始指针(不论是可变还是不可变的原始指针)// 以及如何解引用原始指针let mut num = 5; //这里必须定义为mut 。但也发现了rust编译器的一个问题:会错误体提示要移除这个mut,但其实不能移除。let r1 = &num as *const i32; // 不可变原始指针let r2 = &mut num as *mut i32; // 可变原始指针println!("r1: {}, r2: {}", unsafe { *r1 }, unsafe { *r2 });//让r2+1unsafe {*r2 += 1;}println!("r1: {}, r2: {}", unsafe { *r1 }, unsafe { *r2 });//2.0   创建指向任意内存地址的裸指针// 此处代码需要注释掉,否则后续的代码不会执行。因为原始指针指向的内存地址是不合法的,所以会引发运行时错误。//create_raw_pointer_from_address();
println!("---------------------------------------------------------------");//3.0 使用Box创建原始指针
    create_raw_pointer_use_box();//4.0 如何创建一个空的原始指针
    create_empty_raw_pointer();//5.0 演示如果使用原始指针把一个数组切成两半,且每一半都是可变的切片
    demo_split_slice_to_two_half();
}/*** 创建一个指向任意内存地址的原始指针*/
#[allow(dead_code)]
fn create_raw_pointer_from_address() {let address = 0x012345usize;let r = address as *const i32;unsafe {println!("r: {}", *r);}
}
/*** 使用Box创建原始指针*/
fn create_raw_pointer_use_box() {//回忆下Box指针,我们知道Box指针是一个堆上分配的智能指针。let boxed = Box::new(5);let five = *boxed;println!("five: {}", five);let brave = Box::new(String::from("Rust"));let raw_brave = Box::into_raw(brave);unsafe{println!("raw_brave: {:?}", *raw_brave);}//现在需要手动释放内存,否则会造成内存泄露println!("释放raw_brave");unsafe{drop(Box::from_raw(raw_brave));}println!("释放raw_brave完成");// unsafe{//     println!("raw_brave依然存在,但这个时候它应该是一个空的: {:?}", *raw_brave);// }
}/*** 创建一个空的原始指针-利用std::ptr::null*/
fn create_empty_raw_pointer() {let mut ptr = std::ptr::null::<i32>();if ptr.is_null() {println!("指针ptr是空的");} else {println!("指针ptr不是空的");}let mut value = 5;unsafe {// 将指针指向一个有效的内存地址ptr = &value as *const i32;// 读取指针的值if !ptr.is_null() {println!("指针ptr现在的值是: {}", *ptr);} else {println!("指针ptr现在是空的");}}
}/*** 演示如果使用原始指针把一个数组切成两半,且每一半都是可变的切片*/
fn demo_split_slice_to_two_half() {let mut scores=[10,20,30,40,50];println!("原始scores: {:?}", scores);let (left, right) = unsafe_slice(&mut scores, 3);println!("scores-left: {:?}, scores-right: {:?}", left, right);//现在改改左边第一个,看是不是发生了效果left[0] = 1024;right[0] = 1975;println!("修改后scores: {:?}", scores);  //事实证明了这点:左边改了,所以可变切片可以的let mut poems=["独怜幽草涧边生","上有黄丽深树鸣","春潮带雨晚来急"];let (left, right) = unsafe_slice(&mut poems, 2);println!("left: {:?}, right: {:?}", left, right);
}/*** 使用原始指针创建切片* 这里有非常关键的说明:(来自书本)* Rust 的借用检查器不能理解我们要借用这个 slice 的两个不同部分:它只知道我们借用了同一个 slice 两次。* 本质上借用 slice 的不同部分是可以的,因为结果两个 slice 不会重叠,不过 Rust 还没有智能到能够理解这些* * 这一段至少说明了:rust的编译器也不是万能的,要是万能就太慢了。* 在rust书中,此例子属于:创建不安全代码的安全抽象  。* 即把不安全的操作封装起来,对外提供安全的接口(函数)* @param values 实际是一个数组的完整切片* @param mid 切片中间的索引位置*/
fn unsafe_slice<T>(values: &mut [T], mid: usize)-> (&mut [T], &mut [T]) {//let mut data = [1, 2, 3];//let ptr = &data as *const _;let len = values.len();let ptr = values.as_mut_ptr();   // 切片转为原始指针,且指向切片的起始位置。//方法from_raw_parts_mut用于从原始指针和长度创建一个可变切片。//注意:这里的add方法,是原始指针的一个方法,用于计算偏移后的新地址。//如果切片中保存的是汉字,那么应该加多少了?    unsafe {(slice::from_raw_parts_mut(ptr, mid),slice::from_raw_parts_mut(ptr.add(mid), len - mid),)}
}

 

输出如下:

 

有几点值得说:

  1. 不安全代码必须写在unsafe{}代码块中,否则会报错。
  2. rust的编译器有时候会有问题
  3. 原始指针原则上需要自己手动释放,但也不是所有的都需要。如果是借用来的,可以不要;如果是Box创建的,可以释放
  4. 随意访问内存一块区域,可能会导致程序终止

五、小结

  1. 作为一个重要的补充,原始指针为rust编程提供了灵活性,能够做安全指针所不能完成的一些工作,主要是内存操作和外部函数接口、高级数据结构等
  2. rust有多种方式可以创建原始指针,包括使用引用、Box指针创建、空指针创建
  3. 部分原始指针需要手动释放,部分不需要,主要看如何定义,这个需要特别注意
  4. 原始指针的存在,再一次证明了个性化的、简单的东西能够更加高效,能力也更强,只是对于使用者的水平要求较高

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/896894.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

环境变量Path学习

什么是Path环境变量? “环境变量”和“path环境变量”其实是两个东西,不要混为一谈。 “环境变量”是操作系统工作环境设置的一些选项或属性参数。每个环境变量由变量名和文件路径组成的,可以设置很多个环境变量。 我们一般使用环境变量指定一个文件夹的位置,或一个应用程序…

rust学习二十.1、原始指针(裸指针)

一、前言 指针在前面的篇幅中已经介绍过许多,但主要是智能指针。 智能指针管理堆上的数据,并且受到rust的所有权和借用规则管理(注意,这里的所有权和借用有时候不同于最原始的那种)。 智能指针好歹能管着这些数据,但是rust中存在一些不能使用所有权管理的数据,它们需要利…

深度测评国产 AI 程序员,在 QwQ 和满血版 DeepSeek 助力下,哪些能力让你眼前一亮?

通义灵码上新模型选择功能,不仅引入了 DeepSeek 满血版 V3 和 R1 这两大 “新星”,Qwen2.5-Max 和 QWQ 也强势登场,正式加入通义灵码的 “豪华阵容”。开发者只需在通义灵码智能问答窗口的输入框中,单击模型选择的下拉菜单,便能轻松开启不同模型,畅享个性化服务。通义灵码…

AI Station使用笔记

一、安装maui (1)在104上,终端操作(必须有sudo权限):sudo apt install cifs-utilspip install maui==0.0.32 -i http://10.9.54.102:8888/simple --trusted-host 10.9.54.102 maui login (fang.wang03,密码为开机密码) maui project init RC_Collaboratives (前…

mysql索引浅谈

一. 索引: 索引是数据库中重要的数据结构,主要作用是提高查询的效率。索引相当于书本的目录,即可以快速定位所需数据的位置,而不用逐页查找。 二. 索引底层结构:索引底层主要采用B+树来实现索引的管理。B+树内部分为叶子节点和非叶子节点;非叶子节点主要用来存储索引和指…

java 常用注解记录汇总

1.@Transactional 可放在类或者方法上 2.@JsonFormat 一般用于格式化时间字段属性 3.@JsonProperty 和 @JSONField 前者是Jackson框架后者是阿里的 用法都是属性名之间的映射,场景多用于接口对接 说明: 比如和对方系统进行接口对接时,可能对方的字段名叫做 chinese_name和…

数据库索引简单介绍

数据库索引 基本概念索引本质是一种可以提升数据查询效率的数据结构主要分类 主要分为两类:聚簇(集)索引:一个叶子节点上存储的是索引(主键值)和数据为对应的行数据 非聚簇(集)索引:一个叶子节点上存储的是索引(某一个字段的值)和数据为对应的主键值B+树索引采用的数…

雅马哈机器人E17故障排除案例参考

雅马哈机器人E17故障是一个与电机控制相关的报警,它包含许多特定的故障代码,例如E17.410、E17.411、E17.802、E17.902、E17.905、E17.915等。 故障排除步骤数据备份:出现故障时,尽快备份数据。 诊断工具:使用教学设备或计算机软件进行诊断,浏览系统的所有故障类型和信息,…

记录---前端实现水印功能

🧑‍💻 写在开头 点赞 + 收藏 === 学会🤣🤣🤣前段时间突然接到一个需求,项目中要实现特定水印功能,不同的用户查看时展示用户特有的水印,这样可以在资源外流的时候追究到责任人。 查找搜集了很多方法,有通过CSS伪元素添加水印的、还可以使用CSS重复背景图片、还有…

IDEA2024破解方法

一、下载链接 IDEA2024.2.3: https://www.jetbrains.com/idea/download/other.html 破解脚本和激活码:https://wwpf.lanzouv.com/iZtCB2q6xmwh 二、运行破解脚本 运行 三、输入激活码 激活码位置:

关于举办2024年全国青少年 人工智能创新实践交流展示活动的通知

首页课程项目工具数据集AI 体验读本活动 注册登录“与AI共创未来” 2024年全国青少年人工智能创新实践活动【主办单位】中国科协青少年科技中心、中国青少年科技教育工作者协会、上海人工智能实验室、江苏省科协 【支持单位】中国公众科学素质促进联合体 【承办单位】各省级科…

SRA数据下载丢失质量值信息

001、SRA数据下载丢失质量值信息 使用的转换命令如下: fastq-dump --gzip --split-files -A sample_name sample_name.lite.1zcat SRR17344673_1.fastq.gz | head -n 4 002、解决方法: a、改用EBI数据库:https://www.ebi.ac.uk/ena/browser/viewb、使用 pfastq-dump 进行转…