当前位置: 首页 > news >正文

C++ const、volatile和mutable关键字详解

对于cvconst volatile)类型限定符和关键字mutable在《cppreference》中的定义为:

cv可出现于任何类型说明符中,以指定被声明对象或被命名类型的常量性(constness)或易变性(volatility)。

  • const----------定义类型为常量类型。

  • volatile--------定义类型为易变类型。

mutable用于指定不影响类的外部可观察状态的成员(通常用于互斥体、记忆缓存、惰性求值和访问指令等)。

  • mutable------容许常量类类型对象修改相应类成员。

const

const实际上是一个类型说明,告诉编译器const修饰的目标是不变的,允许编译器对其进行额外的优化,如果后面代码不小心修改它了,就编译失败,告诉用户该目标被意外修改了,提高程序的安全性和可控性。

const修饰普通变量

const修饰过的变量,编译器往往将其作为一个常量进行处理,同时,const修饰的变量在编译阶段会被编译器替换为相应的值,来提高程序的执行效率。

#include <iostream>using namespace std;int main() {const int i = 50;          // 普通常量const static int si = 50;  // 静态常量int* p_int = (int*)&i;  // 强制类型转换为int*int* p_sint = (int*)&si;*p_int = 100;  // 通过非常常量指针修改常量i的值,该行为是C++为未定义行为//*p_sint = 100;//编译不会报错,程序运行时崩溃,且该行为也是C++为未定义行为cout << "i:" << i << ", i的地址: " << &i << endl;//编译器阶段会将常量i替换为50cout << "*p_int:" << *p_int << ", *p_int的地址: " << p_int << endl;return 0;
}

:类型是 const修饰的对象,或常量对象的非可变子对象。这种对象不能被修改:直接尝试这么做是编译时错误,而间接尝试这么做(例如通过到非常量类型的引用或指针修改常量对象)的行为未定义。

输出结果:

i:50, i的地址: 0x7fffffffd9d4
*p_int:100, *p_int的地址: 0x7fffffffd9d4

i*p_int打印出的地址都是0x7fffffffd9d4可以看出,我们偷偷修改i的值成功了(但该行为是C++未定义的行为),但是为何i*p_int的结果却是不同的,这就从侧面证实了const常量具有宏替换的特性,即程序在编译阶段就会其进行部分的替换,例如上述例子中对语句

cout << "i:" << i << ", i的地址: " << &i << endl;

在编译阶段替换为

cout << "i:" << 50 << ", i的地址: " << &i << endl;

因此导致我们输出的i的值为50

同时,当我们想要通过使用非常量指针修改静态常量si时候,编译通过,但是在运行过程中导致程序崩溃(这就是不按规矩办事的后果,使用了C++未定义的行为,编译器也帮不了我们,最终导致程序挂掉)。

const的内部链接性

通常情况下,在未声明为 extern 的变量声明上使用 const 限定符,会给予该变量内部连接(即名称在用于声明变量的文件的外部是不可见的)的特性。即与static类似(详情可参考《C++避坑—关键字static的使用及注意事项》),但是与其不同的是其可以通过extern来改变其内部连接性。

const修饰指针和引用

/********指针********/
//指向const对象的指针
const int* p1; //const修饰的是int,表示p1指向的内容不能被修改
int const* p2; //const修饰的是int,表示p2指向的内容不能被修改//指向对象的const指针
int* const p3; //const修饰的是*,表示p3的指向不能被修改//指向const对象的const指针
const int* const p4; //第一个修饰的是int,第二个修饰的是*,表示p4指向的内容和p4的指向都不能被修改
const int const* p5; //同上,表示p5指向的内容和p5的指向都不能被修改/*注:从上面我们可以总结出来的一个规律:
const优先修饰其左边最近的类型,
如果左边没有,就修饰右边离他最近的那个*//********引用********/
const int a = 0;
//由于a1引用a之后,不能引用其他实体,所以对于const int&可以看作是const int* const
const int& a1 = a; int b = 0;
const int& b1 = b;//C++允许无限定类型的引用/指针能被转换成到 const 的引用/指针

const在类中的应用

非静态数据成员可以被cv限定符修饰,这些限定符写在函数声明中的形参列表之后。其中,带有不同cv限定符(或者没有)的函数具有不同的类型,它们可以相互重载。在具有cv限定符的成员函数内,*this拥有同向的cv限定。例子如下:

#include <iostream>using namespace std;class A {public:A(int a) : a(a){};void show() { cout << "void show(): " << a << endl; }void show() const { cout << "void show() const: " << a << endl; }  // 重载/*这里void show() 等价于 void show(A* this)void show() const 等价于 void show(const A* this)因此该成员函数show()可以被重载*/void set_a(int n) { a = n; }/*void set_a(int n) const{a = n;//此时*this拥有const限制,导致a不能够被修改}//程序报错*/// void print_a() const 等价于 void print_a(const A* this)void print_a() const { cout << "print_a() const: " << a << endl; }private:int a;
};int main() {A a1(1);const A a2(2);a1.show();a2.show();a1.print_a();  // 非const对象可以调用const成员函数// 根本原因是A* this可以隐式转换const A* this// 最终调用void print_a(const A* this)// 即void print_a() consta2.print_a();a1.set_a(2);//   a2.set_a(1);  // 编译报错,const对象不能调用非const成员函数,根本原因是根本原因是const A* this可以隐式转换A* thisreturn 0;
}

输出结果:

void show(): 1
void show() const: 2
print_a() const: 1
print_a() const: 2

对于上述例子我们可以得出:

  • const对象不能调用非const成员函数。 => const成员函数内部不能调用其他非cosnt成员函数。
  • const对象可以调用const成员函数。=> 非cosnt成员函数可以调用其他cosnt成员函数。

volatile

volatile 主要作用是告诉编译器其修饰的目标是易变的,在编译的时候不要去优化它(例如读取的时候,都从目标内存地址读取),防止编译器误认为某段代码中目标是不会被改变,而造成过度优化。

:编译器大部分情况是从内存读取变量的值的,但有时候编译器认为在某段代码中,某个变量的值是没有变化的,所以认为寄存器里的值跟内存里是一样的,为了提高读取速度,编译器可能会从寄存器中读取变量,但是在某些情况下变量的值被其他元素(如另外一个线程或者中断服务)修改,这样导致程序读取变量的值不是最新的,产生异常。

因此,volatile 关键字对于声明共享内存中可由多个进程访问的对象或用于与中断服务例程通信的全局数据区域很有用。如果某个目标被声明为 volatile,则每当程序访问它的时候,编译器都会重新加载内存中的值。 这虽然降低了目标的读取速度,但是保证了目标读取的正确性,这也是保证我们程序可预见性的唯一方法。

下面我们通过一个读取系统时间的例子来看一下volatile在实际开发过程中的应用:

#include <iostream>
#include <ctime>
#include <unistd.h>
// #include <windows.h> //win下为该头文件using namespace std;
int main()
{const time_t time_val = 0; time_t *p_time_t = const_cast<time_t *>(&time_val);time(p_time_t);cout << time_val << endl;// 休眠1ssleep(1); // linux下sleep函数,单位为秒// Sleep(1000); // win下的sleep函数,单位为毫秒time(p_time_t);cout << time_val << endl;return 0;
}

time函数在ctime头文件中定义,其主要作用是获取系统当前时间,其原型如下:

std::time_t time( std::time_t* arg );

返回编码为std::time_t对象的当前日历时间,并将它存储于 arg 所指向的对象,除非 arg 是空指针。

输出结果:

0
0

很明显结果不符合我们的预期,具体原因就是我们上面介绍的const常量具有宏替换的特性,编译器认为这段可以更好的优化,在编译阶段就对其进行了替换。那我们如何修改才能达到我们的实现呢?对,就是添加volatile关键字修饰,具体实现如下:

#include <iostream>
#include <ctime>
#include <unistd.h>
// #include <windows.h> //win下为该头文件using namespace std;
int main()
{volatile const time_t time_val = 0;time_t *p_time_t = const_cast<time_t *>(&time_val);time(p_time_t);cout << time_val << endl;// 休眠1ssleep(1); // linux下sleep函数,单位为秒// Sleep(1000); // win下的sleep函数,单位为毫秒time(p_time_t);cout << time_val << endl;return 0;
}

输出结果:

1680339937
1680339938

从输出结果看出,结果符合我们的预期。

这时候你可能会有疑问:volatile const是什么鬼?const表示time_val是常量,volatile表示time_val是可变的,难道是易变的常量?这不是矛盾吗?

这里我们确实可以将time_val成为易变的常量,只不过常量(不可修改)意味着time_val在其作用域中(这里指的是main函数中)是不可以被改变的,但是在其作用域外面(这里指的是time()函数内)是可以被改变的。volatile const其实是在告诉编译器,在main()函数内,time_valconst的,你帮我看点,我不能随意的修改,但这个值在作用域外可能会被其他东西修改,这玩意也是volatile的,你编译的时候也别优化它了,在每次读取的时候,也老老实实从它的存储位置重新读取吧。

注:volatile constconst volatile是一样的,都代表易变的常量。

volatile修饰常量、指针和引用

volatile修饰常量指针和引用的使用方法域const类似,这里不做过多的解释,但需要注意的是volatile没有像const的内部链接属性。

volatile修饰函数的参数

int sequare(volatile int* ptr)
{return *ptr * *ptr;
}

上述例子是为了计算一个易变类型的int的平方,但是函数内部实现存在问题,因为

return *ptr * *ptr;

其处理逻辑类似下面的情况:

int a = *ptr; 
int b = *ptr;
return a * b;

由于*ptr是易变的,因此ab获取的值可能是不一样的,因此最好采用如下的方式:

int sequare(volatile int* ptr)
{int a = *ptr;return a * a;
}

mutable

mutable主要是为了突破const的某些限制而设定的,即允许常量类型对象相应的类成员可以被修改,其常在非引用非常量类型的非静态数据成员中出现。

在上面的介绍中,我们知道在在获取类某些状态的成员函数中,如果不涉及状态的变更,我们一般会将成员函数声明成const,这将意味着在该函数中,所有的成员函数都不可以被修改,但有些时候我们需要在该const函数中修改一些跟类状态无关的数据乘员,那么这时候就需要mutable发挥作用了,即将该需要修改的成员使用mutable修饰。

#include <iostream>
using namespace std;class A
{
public:A(int data = 0) : int_data(data), times(0) {}void show() const{times++; //因为times被mutable修饰,突破了const的限制cout << "data : " << int_data << endl;}int getNumOfCalls() { return times; }private:int int_data;mutable int times;
};int main()
{A a(1);cout << "a的show()被调用了:" << a.getNumOfCalls() << "次。" << endl;a.show();cout << "a的show()被调用了:" << a.getNumOfCalls() << "次。" << endl;return 0;
}

输出结果:

a的show()被调用了:0次。
data : 1
a的show()被调用了:1次。

上例void show()const修饰后,导致在该函数内类成员不能被修改,但由于timesmutable修饰后,突破了const的限制,使得times在该函数内部可以被修改。

mutable的另一个应用场景就是用来移除lambda函数中按复制捕获的形参的const限制。通常情况下(不提供说明符),复制捕获的对象在lambda体内是 const 的,并且在其内部无法修改被捕获的对象,具体的例子如下:

#include <iostream>
using namespace std;int main() {int a = 0;const int b = 0;auto f1 = [=]() {/*a++;  // 错误,不提供说明符时复制捕获的对象在 lambda 体内是 const 的。b++;  // 错误,同上,且按值传递const也会传递进来*/return a;};auto f2 = [=]() mutable {  // 提供mutable说明符a++;                     // 正确,mutable解除const限制。/*b++;  // 错误,mutable无法突破b本身的const限制*/return a;};cout << a << ", " << b << endl;        // 输出0, 0cout << f1() << ", " << f2() << endl;  // 输出0, 1return 0;
}

总 结

const主要用来告诉编译器,被修饰的变量是不变类型的,在有些情况下可以对其进行优化,同时如果后面代码不小心修改了,编译器在编译阶段报错。在类的应用中,const对象不能调用非const成员函数,非const对象可以调用const成员函数。

volatile主要用来告诉编译器,被修饰变量是易变,在编译的时候不要对其进行优化,读取它的时候直接从其内存地址读取。

同时,constvolatile限定的引用和指针支持下列的隐式转换:

  • 无限定类型的引用/指针能被转换成const的引用/指针
  • 无限定类型的引用/指针能被转换成volatile 的引用/指针
  • 无限定类型的引用/指针能被转换成const volatile 的引用/指针
  • const 类型的引用/指针能被转换成const volatile 的引用/指针
  • volatile 类型的引用/指针能被转换成const volatile 的引用/指针

对于const修饰的成员函数内类成员const的属性,可以通过使用对mutable来解除const限制。同样的,mutable也可以用来移除lambda函数中按复制捕获的形参的const限制。

文章首发公众号:iDoitnow 如果喜欢话,可以关注一下

相关文章:

C++ const、volatile和mutable关键字详解

对于cv&#xff08;const 与 volatile&#xff09;类型限定符和关键字mutable在《cppreference》中的定义为&#xff1a; cv可出现于任何类型说明符中&#xff0c;以指定被声明对象或被命名类型的常量性&#xff08;constness&#xff09;或易变性&#xff08;volatility&#…...

MySQL实验四:数据更新

MySQL实验四&#xff1a;数据更新 目录MySQL实验四&#xff1a;数据更新导读表结构sql建表语句模型图1、 SQL更新&#xff1a;将所有学生的年龄增加1岁代码2、SQL更新&#xff1a;修改“高等数学”课程倒数三名成绩&#xff0c;在原来分数上减5分代码解析3、SQl更新&#xff1a…...

商汤科技推出“日日新SenseNova”,大模型体系赋能人工智能新未来

2023年4月10日&#xff0c;商汤科技SenseTime技术交流日活动在上海举行&#xff0c;分享了以“大模型大算力”推进AGI&#xff08;通用人工智能&#xff09;发展的战略布局&#xff0c;并公布了商汤在该战略下的“日日新SenseNova”大模型体系。 公开信息显示&#xff0c;商汤科…...

【中创AI】斯坦福人工智能年度报告:AI论文发表量中国世界第一!

斯坦福以人为本人工智能研究所 (HAI) 发布了最新一期的 2023 AI 指数 (2023 AI Index) 报告&#xff0c;探讨了过去一年机器学习的发展。 &#xff08;斯坦福HAI于2019年初成立&#xff0c;致力于研究新的AI方法&#xff0c;并研究该技术对社会的影响。其每年发布一份AI指数报…...

Java基础(五)面向对象编程(基础)

学习面向对象内容的三条主线 Java类及类的成员&#xff1a;&#xff08;重点&#xff09;属性、方法、构造器&#xff1b;&#xff08;熟悉&#xff09;代码块、内部类面向对象的特征&#xff1a;封装、继承、多态、&#xff08;抽象&#xff09;其他关键字的使用&#xff1a;…...

寻找CSDN平行世界的另一个你

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 寻找CSDN平行世界的另一个你摘要前言列表测试目的摘要 本文作了一个测试&#xff0c;看看在 CSDN 的博文中&#xff0c;艾特&#xff08;&#xff09;某个好友&#xff0c;TA是否能够…...

ChatGPT的发展对客户支持能提供什么帮助?

多数组织认为客户服务是一种开销&#xff0c;实际上还可以将客户服务看成是一种机会。它可以让你在销售后继续推动客户的价值。成功的企业深知&#xff0c;客户服务不仅可以留住客户&#xff0c;还可以增加企业收入。客户服务是被低估的手段&#xff0c;它可以通过推荐、见证和…...

数据安全评估体系建设

数据安全评估是指对重要数据、个人信息等数据资产的价值与权益、合规性、威胁、脆弱性、防护等进行分析和判断&#xff0c;以评估数据安全事件发生的概率和可能造成的损失&#xff0c;并采取相应的措施和建议。 数据安全评估的重要性和背景 1.国家法律法规下的合规需要 目前数…...

论文阅读 - ANEMONE: Graph Anomaly Detection with Multi-Scale Contrastive Learning

目录 摘要 1 简介 2 问题陈述 3 PROPOSED ANEMONE FRAMEWORK 3.1 多尺度对比学习模型 3.1.1 增强的自我网络生成 3.1.2 补丁级对比网络 3.1.3 上下文级对比网络 3.1.4 联合训练 3.2 统计异常估计器 4 EXPERIMENTS 4.1 Experimental Setup 4.1.1 Datasets 4.1.2 …...

数据密集型应用存储与检索设计

本文内容翻译自《数据密集型应用系统设计》&#xff0c;豆瓣评分高达 9.7 分。 什么是「数据密集型应用系统」&#xff1f; 当数据&#xff08;数据量、数据复杂度、数据变化速度&#xff09;是一个应用的主要挑战&#xff0c;那么可以把这个应用称为数据密集型的。与之相对的是…...

Spring Boot集成Redis实现keyspace监听 | Spring Cloud 34

一、前言 在前面我们通过以下章节对Redis的keyevent&#xff08;键事件通知&#xff09;使用有了基础的了解&#xff1a; Spring Boot集成Redis实现keyevent监听 | Spring Cloud 33 现在开始我们正式学习Redis的keyspace&#xff08;键空间通知&#xff09;&#xff0c;在本…...

如何搭建chatGPT4.0模型-国内如何用chatGPT4.0

国内如何用chatGPT4.0 在国内&#xff0c;目前可以通过以下途径使用 OpenAI 的 ChatGPT 4.0&#xff1a; 自己搭建模型&#xff1a;如果您具备一定的技术能力&#xff0c;可以通过下载预训练模型和相关的开发工具包&#xff0c;自行搭建 ChatGPT 4.0 模型。OpenAI提供了相关的…...

【故障定位】基于多元宇宙算法的主动配电网故障定位方法研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

基于html+css的自适应展示1

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…...

DolphinDB +Python Airflow 高效实现数据清洗

DolphinDB 作为一款高性能时序数据库&#xff0c;其在实际生产环境中常有数据的清洗、装换以及加载等需求&#xff0c;而对于该如何结构化管理好 ETL 作业&#xff0c;Airflow 提供了一种很好的思路。本篇教程为生产环境中 ETL 实践需求提供了一个解决方案&#xff0c;将 Pytho…...

pip3 升级软件包时遇到超时错误解决方法

如果你在使用 pip3 升级软件包时遇到超时错误&#xff0c;可能是因为下载速度缓慢或网络不稳定。以下是解决方法&#xff1a; 更改 pip3 源&#xff1a;你可以切换到其他 pip3 源&#xff0c;例如清华、阿里等等&#xff0c;以提高下载速度。 pip3 install -i https://pypi.tun…...

Linux环境开机自启动

1.制作服务 在/etc/systemd/system/路径下创建kkFile.service文件 cd /etc/systemd/system/ vim kkFile.service2.写入如下内容 [Unit] DescriptionkkFile service [Service] Typeforking ExecStart/sinosoft/yjya/kkFileView-4.0.0/bin/startup.sh [Install] WantedBymulti…...

字节8年测试经验,送给想要学习自动化测试的同学6条建议

我的职业生涯开始和大多数测试人一样&#xff0c;开始接触都是纯功能界面测试。那时候在一家电商公司做测试&#xff0c;做了有一段时间&#xff0c;熟悉产品的业务流程以及熟练测试工作流程规范之后&#xff0c;效率提高了&#xff0c;工作比较轻松&#xff0c;也得到了更好的…...

快速搭建springboot websocket客户端

一、前言WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。HTML5 定义的 WebSocket 协议&#xff0c;能更好的节省服务器资源和带宽&#xff0c;并且能够更实时地进行通讯。HTML5 定义的 WebSocket 协议&#xff0c;能更好的节省服务器资源和带宽&…...

Python 操作 MongoDB 详解

嗨害大家好鸭&#xff01;我是芝士❤ 一、前言 MongoDB属于 NoSQL&#xff08;非关系型数据库&#xff09;&#xff0c; 是一个基于分布式文件存储的开源数据库系统。 二、操作 MongoDB 1. 安装 pymongo python 使用第三方库来连接操作 MongoDB&#xff0c; 所以我们首先安…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...