PTA算法简析

ArkAnalyzer源码初步分析I:https://blog.csdn.net/2302_80118884/article/details/151627341?spm=1001.2014.3001.5501

首先,我们必须明确 PTA 的核心工作:它不再关心变量的“声明类型”,而是为程序中的每一个变量每一个对象字段维护一个**“指向集”(Points-to Set)。这个集合里包含了该变量可能指向的所有对象实例**。分析过程就是通过代码语句,不断更新和传递这些“指向集”。

我们还是使用 Animal 的例子,但这次会引入更复杂的结构。

// --- 我们的基础类 ---
abstract class Animal { abstract sound(): void; }
class Dog extends Animal { sound() { /* 汪汪 */ } }
class Cat extends Animal { sound() { /* 喵喵 */ } }
class Pig extends Animal { sound() { /* 哼哼 */ } } // Pig 还是存在,但我们看看 PTA 能否排除它// --- 更复杂的业务逻辑 ---
class PetStore {bestSeller: Animal | null = null;inventory: Animal[] = [];setBestSeller(pet: Animal) {this.bestSeller = pet;}stockInventory(pets: Animal[]) {this.inventory = pets;}promoteBestSeller() {if (this.bestSeller) {this.bestSeller.sound();}}
}function choosePet(isMorning: boolean): Animal {let chosenPet: Animal;if (isMorning) {chosenPet = new Dog();} else {chosenPet = new Cat();}return chosenPet;
}

现在,让我们分析一个使用这些复杂结构的 main 函数。

场景 1: 通过对象字段(Field Assignments)传递

代码:

function main1() {const store = new PetStore();const myDog = new Dog();// 1. 将 Dog 实例设置到 store 的字段中store.setBestSeller(myDog);// 2. 调用一个方法,该方法会使用这个字段store.promoteBestSeller(); 
}

PTA 分析过程:

  1. const store = new PetStore();

    • PTA 创建一个 PetStore 的实例,我们称之为 PetStore_obj1
    • 它的“指向集账本”记录:store -> { PetStore_obj1 }
  2. const myDog = new Dog();

    • PTA 创建一个 Dog 的实例,称之为 Dog_obj1
    • 账本记录:myDog -> { Dog_obj1 }
  3. store.setBestSeller(myDog);

    • 这是一个方法调用。PTA 会分析 setBestSeller 内部。
    • 内部执行 this.bestSeller = pet;
    • PTA 知道,this 指向 store (即 PetStore_obj1),参数 pet 指向 myDog (即 Dog_obj1)。
    • 于是,它执行了一次数据流传递:将 pet 的指向集 { Dog_obj1 } 赋给了 this.bestSeller 字段。
    • 账本更新:PetStore_obj1.bestSeller -> { Dog_obj1 }
  4. store.promoteBestSeller();

    • PTA 进入 promoteBestSeller 方法,this 仍然指向 PetStore_obj1
    • 分析到 this.bestSeller.sound();
    • PTA 查询 this.bestSeller 的指向集。账本上写着:PetStore_obj1.bestSeller -> { Dog_obj1 }
    • 结论: 指向集里只有一个 Dog 对象。因此,这里的 sound() 调用唯一的目标就是 Dog.sound()

对比 RTA: 如果 RTA 看到代码里别处有 new Cat(),它可能会错误地认为 this.bestSeller.sound() 也可能调用 Cat.sound()。PTA 通过追踪数据流,精确地排除了这种可能。


场景 2: 通过数组/集合(Collections)传递

代码:

function main2() {const store = new PetStore();const todayStock = [new Dog(), new Cat()];// 1. 将一个包含 Dog 和 Cat 的数组设置到 store 的库存中store.stockInventory(todayStock);// 2. 从库存中取出一个宠物(静态分析时无法知道索引是几)const randomPet = store.inventory[0]; randomPet.sound();
}

PTA 分析过程:

这是一个非常关键且具有挑战性的场景。大多数为了性能而设计的 PTA 算法是**“数组不敏感”(Array-insensitive)“集合不敏感”(Collection-insensitive)**的。这意味着它们会将整个数组或集合视为一个“大容器”,而不会区分里面的每个元素。

  1. const todayStock = [new Dog(), new Cat()];

    • PTA 创建 Dog_obj2Cat_obj1
    • PTA 创建一个数组实例 Array_obj1
    • 由于“数组不敏感”,它会将 Dog_obj2Cat_obj1 的信息合并到这个数组容器的指向集中。
    • 账本记录:todayStock -> { Array_obj1 }
    • 同时记录:Array_obj1[*] -> { Dog_obj2, Cat_obj1 }。([*] 表示数组的任意元素)
  2. store.stockInventory(todayStock);

    • 类似于场景1,数据流发生传递。
    • 账本更新:PetStore_obj2.inventory -> { Array_obj1 }
  3. const randomPet = store.inventory[0];

    • PTA 需要确定 randomPet 的指向集。
    • 它首先找到 store.inventory,发现它指向 Array_obj1
    • 然后它需要从 Array_obj1 中取元素。因为分析是静态的,且是“数组不敏感”的,它无法确定索引 [0] 到底会取出谁
    • 因此,它必须做出一个保守的假设:randomPet 可能指向 Array_obj1 容器里的任何一个对象
    • 它将整个容器的指向集 { Dog_obj2, Cat_obj1 } 赋给了 randomPet
    • 账本更新:randomPet -> { Dog_obj2, Cat_obj1 }
  4. randomPet.sound();

    • PTA 查询 randomPet 的指向集,发现是 { Dog_obj2, Cat_obj1 }
    • 结论: 指向集里既有 Dog 也有 Cat。因此,这里的 sound() 调用可能的目标Dog.sound()Cat.sound()

关键点: 在处理集合时,为了保证分析的可行性(在有限时间内完成)和稳健性(Soundness,即不错报任何可能的调用),PTA 会牺牲一定的精度(Precision),通过合并指向集来处理不确定性。


场景 3: 通过条件逻辑和函数返回值(Control Flow & Return Values)

代码:

function main3() {// 1. 调用一个根据条件返回不同类型对象的函数const petOfTheDay = choosePet(new Date().getHours() < 12);// 2. 调用返回对象的方法petOfTheDay.sound();
}

PTA 分析过程:

  1. const petOfTheDay = choosePet(...)

    • PTA 需要分析 choosePet 函数来确定其返回值的指向集。
    • 静态分析器无法知道 new Date().getHours() < 12 的结果是 true 还是 false
    • 因此,它必须分析所有可能的执行路径
      • 路径 A (if 分支): chosenPet = new Dog(); -> chosenPet 指向 { Dog_obj3 }。函数返回 chosenPet
      • 路径 B (else 分支): chosenPet = new Cat(); -> chosenPet 指向 { Cat_obj2 }。函数返回 chosenPet
    • 在函数结束,控制流重新汇合时,PTA 必须合并所有路径的结果。
    • 因此,PTA 为 choosePet 函数计算出一个调用摘要(Summary)choosePet 的返回值指向集是 { Dog_obj3, Cat_obj2 }
    • 当分析 main3 中的调用时,它直接应用这个摘要。
    • 账本更新:petOfTheDay -> { Dog_obj3, Cat_obj2 }
  2. petOfTheDay.sound();

    • PTA 查询 petOfTheDay 的指向集,发现是 { Dog_obj3, Cat_obj2 }
    • 结论: 这里的 sound() 调用可能的目标Dog.sound()Cat.sound()

总结

PTA 通过以下方式处理复杂情况:

  1. 数据流跟踪: 它能跟踪对象实例如何通过变量赋值、参数传递、字段存储和函数返回在程序中“流动”。
  2. 路径合并: 当遇到 if/else 或循环等控制流分支时,它会分析所有可能路径,然后在路径汇合点合并结果(指向集)。这保证了不会遗漏任何可能性。
  3. 保守抽象: 在遇到它无法精确分析的情况时(如数组索引、复杂的反射调用等),它会做出保守的假设(例如,把整个数组看成一个整体),这会牺牲精度,但能保证结果的“稳健性”(即结果集一定包含了所有真实运行时的可能调用)。

所以,PTA 的强大之处在于,即使代码逻辑错综复杂,它也能给出一个包含所有真实调用且尽可能精确的调用图。它是进行更高级程序分析(如安全漏洞扫描、资源泄露检测)的基石,因为这些分析都依赖于一个准确的调用关系和数据流动图。

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

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

相关文章

Vue 3 中监听多个数据变化的几种方法

1. 使用 watch监听多个 ref/reactive 数据import { ref, watch } from vueexport default {setup() {const count ref(0)const name ref()const user reactive({ age: 20 })// 监听多个数据源watch([count, name, () > user.age], // 数组形式传入多个数据源([newCount, …

第 2 篇:Java 入门实战(JDK8 版)—— 编写第一个 Java 程序,理解基础运行逻辑

用 IntelliJ IDEA 写第一个 Java 8 程序&#xff1a;Hello World 实操指南 作为 Java 初学者&#xff0c;“Hello World” 是你接触这门语言的第一个里程碑。本文会聚焦 Java 8&#xff08;经典 LTS 版本&#xff0c;企业级开发常用&#xff09; 和 IntelliJ IDEA&#xff08;当…

【GPT入门】第67课 多模态模型实践: 本地部署文生视频模型和图片推理模型

【GPT入门】第67课 多模态模型实践&#xff1a; 本地部署文生视频模型和图片推理模型1. 文生视频模型CogVideoX-5b 本地部署1.1 模型介绍1.2 环境安装1.3 模型下载1.4 测试2.ollama部署图片推理模型 llama3.2-vision2.1 模型介绍2.2 安装ollama2.3 下载模型2.4 测试模型2.5 测试…

C++初阶(6)类和对象(下)

1. 再谈构造函数&#xff08;构造函数的2个深入使用技巧&#xff09; 1.1 构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值。 虽然上述构造函数调用之后&#xff0c;对象中已经有了一个初始值&#xff0c;…

容器文件描述符热迁移在云服务器高可用架构的实施标准

在云计算环境中&#xff0c;容器文件描述符热迁移技术正成为保障业务连续性的关键解决方案。本文将深入解析该技术在云服务器高可用架构中的实施标准&#xff0c;涵盖技术原理、实现路径、性能优化等核心维度&#xff0c;为构建稳定可靠的容器化基础设施提供系统化指导。 容器文…

毫米波雷达液位计如何远程监控水位?

引言毫米波雷达液位计作为一种高精度、非接触式的水位监测设备&#xff0c;正逐渐成为智慧水务、环境监测等领域的关键工具。其通过先进的调频连续波&#xff08;FMCW&#xff09;技术&#xff0c;实现5mm的测量精度&#xff0c;并支持多种远程通信方式&#xff0c;使用户能够实…

关于 C++ 编程语言常见问题及技术要点的说明

关于 C 编程语言常见问题及技术要点的说明C 作为一门兼具高效性与灵活性的静态编译型编程语言&#xff0c;自 1985 年正式发布以来&#xff0c;始终在系统开发、游戏引擎、嵌入式设备、高性能计算等领域占据核心地位。随着 C 标准&#xff08;如 C11、C17、C20&#xff09;的持…

【Qt QSS样式设置】

Qt中的QSS样式设置流程 Qt Style Sheets (QSS) 是Qt框架中用于自定义控件外观的样式表语言&#xff0c;其语法类似于CSS。以下是QSS的设置流程和示例。 QSS设置流程 1. 创建QSS样式表文件或字符串 首先&#xff0c;需要创建QSS样式表&#xff0c;可以是一个单独的.qss文件&…

使用 Apollo TransformWrapper 生成相机到各坐标系的变换矩阵

使用 Apollo TransformWrapper 生成相机到各坐标系的变换矩阵一、背景二、原理1、什么是变换矩阵&#xff1f;2、为什么需要变换矩阵&#xff1f;3、Apollo 中的坐标系4、Apollo TransformWrapper三、操作步骤1. 设置车辆参数2. 启动静态变换发布3. 查看变换信息4. 播放记录数据…

硬件(十)IMX6ULL 中断与时钟配置

一、OCP 原则&#xff08;开闭原则&#xff09;对代码扩展是开放的&#xff0c;允许通过新增代码来扩展功能&#xff1b;对代码的修改是关闭的&#xff0c;尽量避免直接修改已有稳定运行的代码&#xff0c;以此保障代码的稳定性与可维护性。二、中断处理&#xff08;一&#xf…

打工人日报#20250913

打工人日报#20250913 周六&#xff0c;回杭州了&#xff0c;这边居然下雨。 阅读 《小米创业思考》 第七章 技术为本 其中的技术介绍算是比较详细的&#xff0c;架构也很清晰&#xff0c;有一种对自己家产品如数家珍的感觉&#xff0c;对于架构也是经常思考的感觉感恩 和namwei…

【面试题】RAG核心痛点

1. 文档切分粒度不好把控&#xff0c;既担心噪声太多又担心语义信息丢失 这是一个经典难题。切分粒度过大&#xff0c;单个chunk包含过多无关信息&#xff08;噪声&#xff09;&#xff0c;会干扰LLM理解核心内容&#xff1b;切分过小&#xff0c;则可能割裂句子或段落的完整语…

网络安全与iptables防火墙配置

iptables基本概念iptables是Linux系统中强大的防火墙工具&#xff0c;它工作在用户空间&#xff0c;通过命令行界面与内核空间的netfilter框架交互&#xff0c;实现数据包过滤、网络地址转换(NAT)等功能。Web服务器防火墙配置实例以下是针对Web服务器的iptables配置步骤&#x…

qt中给QListWidget添加上下文菜单(快捷菜单)

步骤 添加customContextMenuRequested信号的槽函数&#xff0c;添加后&#xff0c;在QListWidget上单击右键&#xff0c;无法响应&#xff0c;还必须执行下面操作&#xff1b;设置QListWidget上下文菜单策略为Qt::CustomContextMenu 如下&#xff1a;

一款好看的jQuery前端框架-HisUI

HisUI&#xff1a;一款基于EasyUI的前端组件类库&#xff0c;让web开发更迅速、简单。 HisUI官网文档

【Docker】P3 入门指南:运维与开发双重视角

目录Docker入门&#xff1a;运维与开发运维视角Docker 架构概述Docker 镜像镜像概念理解查看和管理镜像拉取镜像镜像标识容器管理启动容器容器内操作容器的后台运行多容器管理重新进入运行中的容器容器生命周期管理开发视角容器化思维示例&#xff1a;基于 Nginx 镜像构建简单 …

第六届大数据、人工智能与物联网工程国际会议(ICBAIE 2025)

重要信息 时间&#xff1a;2025年10月17-19日 地点&#xff1a;中国上海 官网&#xff1a;www.icbaie.net 征稿主题 1. 大数据与云计算 2. 人工智能技术与应用 3. 机器人科学与工程 4. 物联网与传感器技术 5. 其他 大数据、人工智能与物联网 引言 在数字化转型的时代…

Docker存储卷(Volume)核心概念、类型与操作指南

文章目录一、存储卷概念二、存储卷分类2.1 管理卷2.2 绑定数据卷2.3 临时数据卷三、MySQL灾难恢复四、存储卷的局限性一、存储卷概念 什么是存储卷&#xff1f;   Docker 存储卷 是 Docker 容器中用于持久化存储数据的独立文件系统区域。它独立于容器的联合文件系统&#xf…

Electron 原生模块集成:使用 N-API

引言&#xff1a;原生模块集成在 Electron 开发中的 N-API 核心作用与必要性 在 Electron 框架的扩展开发中&#xff0c;原生模块集成是提升应用性能和功能边界的关键技术&#xff0c;特别是使用 N-API&#xff08;Node-API&#xff09;编写和集成 C 原生模块&#xff0c;更是 …

android组包时会把从maven私服获取的包下载到本地吗

Android项目在构建&#xff08;组包&#xff09;时&#xff0c;Gradle会自动将从Maven私服&#xff08;或任何配置的仓库&#xff09;获取的依赖包&#xff08;AAR、JAR等&#xff09;下载到本地的Gradle缓存目录中。 下面详细解释这个过程和相关的概念&#xff1a; 详细过程声…