Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
C++中如何检测数组指针的连续性 内存地址算术验证方法_创想鸟

C++中如何检测数组指针的连续性 内存地址算术验证方法

c++++中检测数组指针的连续性是通过内存地址算术验证数据是否紧邻存储。1. 对于t类型的指针,连续性可通过比较相邻元素地址差是否等于sizeof(t)来判断,如使用函数is_contiguous_pair或verify_sequence_continuity进行逐对检查;2. 对于t类型的指针数组,需验证指针本身在内存中的连续性,即相邻指针地址差是否等于sizeof(t),这可通过verify_pointer_array_continuity函数实现;这些方法基于指针算术定义,仅适用于同一内存块内的指针比较,且无法验证内存有效性或所有权,在实际开发中推荐优先使用std::vector和std::array等标准库容器以避免手动验证带来的风险。

C++中如何检测数组指针的连续性 内存地址算术验证方法

C++中检测数组指针的连续性,说白了,就是通过内存地址算术来验证一系列数据在内存中是否紧挨着排布。核心思想是,如果一个指针

p

指向类型

T

的一个元素,那么

p + 1

理论上应该指向

p

后面紧邻的下一个

T

类型的元素。这个“紧邻”的距离,精确来说,就是

sizeof(T)

字节。通过比较地址,我们就能判断这种连续性是否成立。

C++中如何检测数组指针的连续性 内存地址算术验证方法

解决方案

要验证C++中数组指针的连续性,我们主要依赖于指针算术的特性。当一个指针

ptr

指向一个数组(或内存块)中的某个元素时,

ptr + N

会移动

N * sizeof(*ptr)

个字节,指向数组中第

N

个元素(从

ptr

算起)。如果这个算术结果与实际观察到的下一个元素的地址一致,那么我们就可以认为它们是连续的。

我们来看几种具体场景:

立即学习“C++免费学习笔记(深入)”;

C++中如何检测数组指针的连续性 内存地址算术验证方法

*1. 验证 `T` 指向的内存块是否连续(最常见情况)**

假设你有一个

T*

类型的指针

data_ptr

,你怀疑它可能指向一个由

N

T

类型元素组成的连续内存块。你无法直接验证整个块,但你可以验证其中任意两个相邻元素之间的关系。

C++中如何检测数组指针的连续性 内存地址算术验证方法

#include #include #include  // For uintptr_t// 假设我们有一个函数,它返回一个指向某个元素的指针// 但我们不确定它是否来自一个连续的数组T* get_some_element_ptr();// 示例函数:验证两个相邻指针的连续性template bool is_contiguous_pair(T* p1, T* p2) {    // 检查 p2 的地址是否恰好在 p1 之后 sizeof(T) 字节    // 强制转换为 char* 是为了进行字节级别的地址比较,避免类型T的对齐或填充影响    // 当然,对于T*类型,p1 + 1 == p2 已经足够,但转换为字节更直观地体现了“连续”    return (reinterpret_cast(p2) - reinterpret_cast(p1)) == sizeof(T);}// 示例:验证一个指针序列的连续性template bool verify_sequence_continuity(T* start_ptr, size_t count) {    if (count <= 1) return true; // 单个元素或空序列总是连续的    for (size_t i = 0; i < count - 1; ++i) {        // 理论上,start_ptr[i+1] 的地址应该等于 start_ptr[i] + 1        // 也就是 start_ptr[i] 的地址加上 sizeof(T)        if (reinterpret_cast(start_ptr + (i + 1)) !=             (reinterpret_cast(start_ptr + i) + sizeof(T))) {            return false; // 发现不连续        }    }    return true; // 所有相邻元素都连续}// 实际应用示例int main() {    std::vector vec = {10, 20, 30, 40, 50};    int* vec_data = vec.data(); // std::vector::data() 保证连续性    std::cout << "Vector data continuity check:" << std::boolalpha << verify_sequence_continuity(vec_data, vec.size()) << std::endl;    // 模拟一个不连续的场景(例如,通过单独分配)    int* p1 = new int(100);    int* p2 = new int(200);    int* p3 = new int(300);    std::cout << "p1 and p2 are contiguous? " << is_contiguous_pair(p1, p2) << std::endl; // 几乎总是不连续的    std::cout << "p2 and p3 are contiguous? " << is_contiguous_pair(p2, p3) << std::endl; // 几乎总是不连续的    delete p1;    delete p2;    delete p3;    // 另一个例子:C风格数组    int arr[5] = {1, 2, 3, 4, 5};    std::cout << "C-style array continuity check: " << verify_sequence_continuity(arr, 5) << std::endl;    return 0;}

2. 验证 `T` (指针的数组)本身的连续性**

如果你的“数组指针”指的是一个

T**

类型,即一个指向指针的指针,而你想要验证的是 这些指针本身 在内存中是否连续排列(形成一个指针数组),那么原理是一样的,只是

sizeof

的对象变成了

sizeof(T*)

#include #include #include template bool verify_pointer_array_continuity(T** ptr_array_start, size_t count) {    if (count <= 1) return true;    for (size_t i = 0; i < count - 1; ++i) {        // 检查 ptr_array_start[i+1] 的地址是否等于 ptr_array_start[i] + sizeof(T*)        // 注意这里是 T* 的数组,所以步长是 sizeof(T*)        if (reinterpret_cast(ptr_array_start + (i + 1)) !=             (reinterpret_cast(ptr_array_start + i) + sizeof(T*))) {            return false; // 指针数组本身不连续        }    }    return true;}int main() {    int val1 = 10, val2 = 20, val3 = 30;    int* p_arr[3]; // C风格的指针数组,通常是连续的    p_arr[0] = &val1;    p_arr[1] = &val2;    p_arr[2] = &val3;    std::cout << "Pointer array (p_arr) continuity check: "               << std::boolalpha << verify_pointer_array_continuity(p_arr, 3) << std::endl;    // 动态分配的指针数组    int** dynamic_p_arr = new int*[2];    dynamic_p_arr[0] = new int(100);    dynamic_p_arr[1] = new int(200);    std::cout << "Dynamic pointer array continuity check: "              << std::boolalpha << verify_pointer_array_continuity(dynamic_p_arr, 2) << std::endl;    delete dynamic_p_arr[0];    delete dynamic_p_arr[1];    delete[] dynamic_p_arr;    return 0;}

为什么我们需要验证指针的连续性?

说实话,这问题听起来有点学院派,但实际开发中,你还真可能碰到需要确认某个指针是否真的指向了一块连续内存的场景。我个人经验告诉我,这种检查往往出现在调试那些从外部接口或者底层C库拿到的内存时,比如一个函数返回了

void*

,并声称这是一个

N

个元素的数组。你得确保这个承诺是真实的,否则后续的指针算术操作就会导致未定义行为,程序直接崩溃给你看。

还有,高性能计算或者嵌入式系统开发里,内存布局的连续性对缓存效率和DMA(直接内存访问)至关重要。如果数据不连续,可能导致性能急剧下降,或者硬件根本无法正确处理。这时候,在关键路径上加个断言或者检查,就能提前发现问题。

此外,有时候你会自己实现一些容器或者内存池,为了验证你的分配策略是否真的提供了连续的内存块,这种检测方法就派上用场了。它能帮你排除是算法逻辑问题还是内存布局问题。

内存地址算术的局限性与陷阱

别以为这事儿简单,里头的坑可不少。

首先,也是最重要的一点:指针算术只在指向同一个数组(或分配的内存块)的元素,以及数组末尾的“一个”位置时是明确定义的。你不能拿两个完全不相关的指针

p1

p2

(比如它们是两次独立的

new

操作的结果),然后去比较

p2 - p1

或者

p1 + 1 == p2

。这样做,编译器可能不会报错,但行为是未定义的。这意味着你的程序可能在你的机器上跑得好好的,到客户那里就随机崩溃了,这种问题最难调试。

其次,内存对齐。虽然

sizeof(T)

通常会考虑类型

T

的大小和填充(padding),但如果你在进行非常底层的操作,比如直接处理

char*

然后

reinterpret_cast

回去,你需要确保你的操作不会破坏原始类型的对齐要求。不过,对于标准的

T*

指针算术,C++语言本身会处理好对齐问题,所以

ptr + 1

总是指向下一个正确对齐的

T

实例。

再来,这种方法无法验证内存的有效性或所有权。它只能告诉你地址是不是连续的,但不能告诉你这块内存是否仍然有效(比如已经被

delete

了),或者你是否有权限访问它。你可能在检查一个已经释放的内存区域,然后得到一个“连续”的结果,但访问它仍然会导致段错误。

最后,编译器优化有时会让你感到困惑。当编译器知道两个指针是来自同一个数组时,它可能会进行激进的优化。但如果你引入了

reinterpret_cast

或者一些看似无关的操作,优化器可能会失去这种上下文,导致意想不到的结果。所以,在进行这类底层操作时,最好保持代码清晰,避免过度复杂的指针体操。

针对不同类型指针的连续性检测

我们前面已经提到了

T*

T**

的情况,但还有些细节值得展开。

对于

T*

这种最直接的“指向数据”的指针,检测其连续性就是看

(ptr + 1)

的地址是否等于

ptr

的地址加上

sizeof(T)

。这在处理

char*

int*

struct MyData*

等时都适用。

// 验证一个 int* 指针是否指向一个连续的 int 序列int my_data[3] = {1, 2, 3};int* p_start = my_data;// 检查 p_start[0] 和 p_start[1] 是否连续bool is_0_1_contiguous = (reinterpret_cast(p_start + 1) ==                           (reinterpret_cast(p_start) + sizeof(int)));// is_0_1_contiguous 会是 true

对于

T**

,也就是“指向指针的指针”,检测的是它所指向的那些指针在内存中是否连续。这通常发生在你有

int* arr_of_ptrs[N]

这样的结构时。这里的

arr_of_ptrs

本身是一个数组,里面存放的是

int*

类型的指针。验证的就是

arr_of_ptrs[0]

arr_of_ptrs[1]

的地址是否相差

sizeof(int*)

// 验证一个 int** 指针是否指向一个连续的 int* 序列int a=1, b=2;int* ptrs[2];ptrs[0] = &a;ptrs[1] = &b;int** pp_start = ptrs;// 检查 pp_start[0] 和 pp_start[1] 是否连续bool is_ptrs_contiguous = (reinterpret_cast(pp_start + 1) ==                            (reinterpret_cast(pp_start) + sizeof(int*)));// is_ptrs_contiguous 会是 true,因为 ptrs 是一个连续的C风格数组

需要注意的是,

T**

验证的是 指针数组本身 的连续性,而不是 指针数组中每个指针所指向的数据 的连续性。

ptrs[0]

指向

a

ptrs[1]

指向

b

a

b

在内存中很可能是不连续的,但这不影响

ptrs

数组本身的连续性。这是两个不同的概念。

至于其他类型的指针,比如函数指针(

void (*func_ptr)()

)或者成员指针(

int MyClass::*member_ptr

),它们通常不适用于这种“连续性”的讨论。函数指针指向的是代码段中的指令,而成员指针更像是一种偏移量,它们不构成传统意义上的“数组”,因此内存地址算术验证连续性在这里没有实际意义。

实际应用中的最佳实践与替代方案

每次写到指针,我总会想起那句老话:“权力越大,责任越大”。指针就是这样,给了你直接操作内存的权力,但也把所有风险都推给了你。

在现代C++中,如果你能避免直接裸指针操作,那通常是最好的选择。

首选

std::vector

std::array

这是C++标准库提供的最强大的武器。

std::vector

动态大小,

std::array

固定大小,但它们都 保证了其元素在内存中是连续存储的。你可以放心地使用

vec.data()

或者

arr.data()

获取一个

T*

指针,然后进行指针算术,因为你知道它们背后是连续的内存块。这省去了你自己手动验证的麻烦,也大大降低了出错的概率。

std::vector temps(100);// temps.data() 指向的内存是连续的,无需验证double* first_temp = temps.data();double* second_temp = first_temp + 1; // 绝对安全且正确

使用迭代器: 对于大多数容器,C++提供了迭代器。迭代器提供了一种抽象的方式来遍历序列,它们内部可能使用指针,也可能不是。使用迭代器可以让你专注于算法逻辑,而不是底层内存布局。对于像

std::vector

这样的连续容器,

std::vector::iterator

通常就是

T*

,所以它的行为和指针算术是吻合的。

断言(

assert

)进行调试: 如果你确实需要处理裸指针,并且对某个指针序列的连续性有强烈的假设,那么在开发和测试阶段,使用

assert

来验证这些假设是非常有用的。

assert

只在调试模式下生效,生产模式下会被移除,避免了性能开销。

#include void process_data(int* data, size_t count) {    // 假设 data 指向一个连续的 int 数组    if (count > 1) {        assert(reinterpret_cast(data + 1) ==                (reinterpret_cast(data) + sizeof(int)) &&                "Data array is not contiguous as expected!");    }    // ... 继续处理数据}

清晰的文档和契约: 如果你的函数接收一个裸指针,务必在文档中明确指出它期望的内存布局(例如,是否需要连续,需要多少元素)。这是协作开发中的基本要求,能避免很多不必要的调试。

总而言之,虽然手动验证指针连续性是理解内存工作方式的重要一环,但在日常C++开发中,更多地应该依赖标准库容器和现代C++的范式来保证内存的正确性和安全性。只有在特定场景下(例如与C库交互、底层系统编程、或自定义内存管理),这种直接的地址算术验证才显得尤为重要。

以上就是C++中如何检测数组指针的连续性 内存地址算术验证方法的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1469891.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
如何用智能指针管理第三方库资源 自定义删除器实践案例
上一篇 2025年12月18日 18:10:53
如何用C++实现桥接模式 分离抽象与实现的设计技巧
下一篇 2025年12月18日 18:11:13

相关推荐

  • ChatExcel进行数据分类_ChatExcel数据自动分类与标签管理

    答案:通过内置规则、AI智能打标、多维度交叉分类及手动修正四步实现ChatExcel自动分类与标签管理。首先设定字段匹配规则自动归类数据;其次启用智能打标功能分析文本生成语义标签;再通过组合多个属性构建交叉分类矩阵实现精细化管理;最后支持人工干预修正异常项并同步更新数据库,提升分类准确性与管理效率。…

    2026年9月22日
    000
  • 抖音流量助推怎么来的?平台流量助推什么意思

    近年来,短视频平台迅速崛起,其中以抖音最为突出。作为一个专注于短视频内容分享的平台,抖音凭借其智能算法和多元化的内容生态,吸引了海量用户。而“抖音流量助推”这一概念,也成为众多创作者和品牌实现快速成长的重要工具。本文将带您深入了解抖音流量助推背后的运作机制。 一、抖音流量助推的核心机制 1. 推荐算…

    2026年9月22日
    100
  • VSCode配置C++项目环境 新手必看VSCode搭建C++教程

    答案:在VSCode中配置C++环境需安装MinGW-w64编译器并将其路径加入系统环境变量,安装VSCode的C/C++扩展以支持代码补全和调试,通过tasks.json配置编译任务,指定g++路径及编译参数,再通过launch.json配置调试任务,设置gdb调试器路径和程序输出路径,确保头文件…

    2026年9月22日
    200
  • 如何使用DeepSpeed训练AI大模型?大规模模型训练的优化技巧

    DeepSpeed通过ZeRO等技术突破显存限制,实现大模型高效训练。它采用ZeRO-1/2/3分级优化,分别对优化器状态、梯度和参数进行分区,显著降低单卡显存占用;结合混合精度、梯度累积和CPU/NVMe卸载进一步节省资源。同时集成流水线并行与张量并行,支持多维并行策略协同,使万亿参数模型训练在普…

    2026年9月22日
    000
  • PHP 表单验证:确保 HTML select 下拉菜单已正确选择非默认选项

    本文将详细介绍如何在 PHP 后端对 HTML select 下拉菜单进行有效验证,确保用户选择了非默认选项。我们将探讨常见的验证误区,并提供一个简洁高效的解决方案,通过检查 $_POST 数据来判断用户是否已做出有效选择,从而避免表单提交无效数据,提升用户体验和数据准确性。 在构建 web 表单时…

    2026年9月22日
    200
  • python 基准测试(cProfile kcachegrind line_profiler memory_profiler)

    learn from 《python高性能(第2版)》 类似工具:pycharm profile对函数调用效率进行测试 1. 例子 一个圆周运动的动画 代码语言:javascript代码运行次数:0运行复制 from matplotlib import pyplot as pltfrom matpl…

    2026年9月22日
    200
  • NvidiaCanvas的AI混合工具如何使用?创作智能画作的详细教程

    NVIDIA Canvas是一款基于AI的智能图像生成器,它将用户涂鸦的材质色块实时转化为逼真风景,核心在于语义理解与风格化合成。其工作流程包括选择材质笔刷、在输入画布绘制概念图、利用图层与风格预设快速迭代,并导出成果。相比传统绘画工具,Canvas优势在于高效生成、降低创作门槛、支持快速探索与创意…

    2026年9月22日
    300
  • VSCode安装C/C++开发环境 最新VSCode配置C语言教程详解

    答案:搭建VSCode的C/C++环境需安装编译器、C/C++扩展并配置项目文件。首先安装MinGW(Windows)、Clang(macOS)或GCC(Linux),配置环境变量并验证;然后在VSCode中安装Microsoft的C/C++扩展;最后创建.c_cpp_properties.json…

    2026年9月22日
    300
  • 在Java中如何通过Stream实现交集与差集

    交集可通过filter结合contains获取两集合共有元素,差集则保留一个集合中不在另一集合的元素,示例使用list1.stream().filter(list2::contains)得[3,4],filter(e->!list2.contains(e))得[1,2],建议将list2转为H…

    2026年9月22日
    000
  • Java Swing中按钮与文本框事件处理的实践指南

    本文将深入探讨Java Swing中ActionListener的正确使用方法,指导开发者如何为GUI按钮和文本框实现事件监听,从而处理用户输入、执行计算并实时更新界面。文章将重点讲解如何在actionPerformed方法中获取用户输入、进行类型转换、处理潜在异常,并提供一个完整的计算器示例来演示…

    2026年9月22日
    100
  • MySQL查询缓存配置及性能_MySQL重复查询响应速度提升

    MySQL查询缓存配置及性能_MySQL重复查询响应速度提升MySQL查询缓存配置及性能_MySQL重复查询响应速度提升MySQL查询缓存配置及性能_MySQL重复查询响应速度提升MySQL查询缓存配置及性能_MySQL重复查询响应速度提升

    mysql查询缓存已不适用于现代应用场景,尤其在8.0版本中被彻底移除。它仅适合读多写少、数据几乎不变的静态查询,通过内存直接返回结果提升性能;但在数据频繁更新时,因基于表级的缓存失效机制,每次写操作都会清空相关缓存,导致频繁重建缓存并消耗大量cpu资源,形成性能瓶颈。此外,sql语句匹配严格、内存…

    2026年9月22日 用户投稿
    400
  • Linux平台下的Eclipse配置

    在linux平台上配置eclipse时,可能会遇到一些常见的问题和优化需求。本文将详细介绍如何解决这些问题,并提供优化eclipse的建议。 启动Eclipse报错 启动Eclipse时,如果遇到以下错误: A Java Runtime Environment (JRE) or Java Devel…

    2026年9月22日
    000
  • 怎样在iPhone情侣模式中分享视频?快速上传和同步的实用方法

    怎样在iPhone情侣模式中分享视频?快速上传和同步的实用方法怎样在iPhone情侣模式中分享视频?快速上传和同步的实用方法怎样在iPhone情侣模式中分享视频?快速上传和同步的实用方法怎样在iPhone情侣模式中分享视频?快速上传和同步的实用方法

    最实用的方法是使用iCloud共享相簿,它支持情侣间视频快速上传与实时同步。首先双方需开启iCloud照片中的共享相簿功能,然后创建专属相簿并邀请对方加入。此后,任一方添加的视频会自动同步至对方设备,且不占用个人iCloud空间。相比AirDrop(限近距离)、即时通讯工具(压缩画质)或云盘(占存储…

    2026年9月22日 用户投稿
    000
  • VSCode如何实现代码混淆 VSCode保护源代码的加密方案

    代码混淆无法通过vscode直接实现,需借助外部工具或构建流程。1. 使用构建工具如webpack或rollup配合terser等混淆器,在打包时自动混淆代码,通过vscode配置tasks.json运行构建任务。2. 可选vscode插件辅助,但应评估其安全性、混淆强度、性能影响、配置灵活性及社区…

    2026年9月22日
    100
  • AdobeIllustrator如何导出AI生成的图片?矢量图像保存的步骤

    首先明确输出需求,再选择“文件>导出>导出为…”或“文件>存储为…”,根据用途选取合适格式如SVG、PDF或AI,确保矢量信息保留,参数设置需匹配目标平台要求。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ Adob…

    2026年9月22日
    000
  • PHP面向对象开发:解决父类构造器参数在嵌套子对象方法中丢失的问题

    在PHP面向对象编程中,我们经常会遇到类继承和对象组合的场景。一个常见的问题是,当父类构造函数接收参数并用于初始化内部的子对象时,该子对象的方法在后续调用中可能无法正确访问到这些参数,甚至显示为null。本文将深入探讨这一问题,并提供两种有效的解决方案。 引言与问题阐述 考虑一个典型的web应用架构…

    2026年9月22日
    000
  • 使用MockWebServer对FeignClient进行单元测试

    本文详细阐述了如何利用Spring Cloud LoadBalancer和MockWebServer对FeignClient进行高效单元测试。通过在测试配置中动态注册MockWebServer实例,并将其作为FeignClient的服务发现目标,开发者可以精确模拟后端API的行为,包括各种HTTP响…

    2026年9月22日
    000
  • Sublime快速定位MySQL死锁与阻塞问题_提高系统稳定性与并发处理能力

    Sublime快速定位MySQL死锁与阻塞问题_提高系统稳定性与并发处理能力Sublime快速定位MySQL死锁与阻塞问题_提高系统稳定性与并发处理能力Sublime快速定位MySQL死锁与阻塞问题_提高系统稳定性与并发处理能力Sublime快速定位MySQL死锁与阻塞问题_提高系统稳定性与并发处理能力

    mysql死锁和阻塞问题可通过sublime分析日志排查。1.使用关键词搜索及正则匹配快速定位死锁信息块;2.通过查找waiting状态事务并折叠无关内容聚焦阻塞链;3.结合general log与事务id查找sql执行顺序;4.利用语法高亮、列选择等功能提升日志可读性。这些方法能有效提高排查效率。…

    2026年9月22日 用户投稿
    100
  • Canva中AI生成图片如何导出?教你快速保存设计作品的方法

    答案:Canva中导出AI生成图片的操作与普通图片相同,点击右上角“分享”按钮,选择“下载”,可选PNG、JPG、PDF、SVG、MP4或GIF等格式;为保证画质,建议优先选用PNG格式,避免有损压缩,同时选择高分辨率和合适尺寸,Pro用户可进一步调整质量与透明背景设置;除下载外,还可通过分享链接、…

    2026年9月22日
    800
  • Linux系统中文件属性和权限实战操作

    Linux系统中文件属性和权限实战操作Linux系统中文件属性和权限实战操作Linux系统中文件属性和权限实战操作Linux系统中文件属性和权限实战操作

    —–原本今天的文章是昨天晚上就要更新的,但是由于昨天晚上下班回到住的地方,发现停电了,所以就没写成。今天是在上一篇文章–linux系统中文件类型的基础上,继续进行深入的学习。好了,直接开干。 一、文件的操作权限: 1、在这之前我想还是很有必要介绍对文件的操作权限(…

    2026年9月22日 用户投稿
    200

发表回复

登录后才能评论
关注微信