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

基于 Clang和LLVM 的 C++ 代码静态分析工具开发教程

基于 Clang和LLVM 的 C++ 代码静态分析工具开发教程

简介

静态代码分析是一种在不实际运行程序的情况下对源代码进行分析的技术。它可以帮助开发者在编译之前发现潜在的错误、安全漏洞、性能问题等。
在 C++ 开发中,有几种常用的静态代码分析工具,它们可以帮助开发者在编译前发现潜在的代码问题、提高代码质量和安全性。以下是几种常见的静态分析工具:

  1. Clang Static Analyzer:

    • 简介:Clang Static Analyzer 是基于 LLVM/Clang 的一个开源静态分析工具,专门用于 C 和 C++ 代码。
    • 特点:能够发现内存泄漏、空指针解引用、整数溢出等问题。
    • 使用:可以通过 Clang 提供的命令行接口使用,也可以集成到 IDE 中(如 Xcode)。
  2. Cppcheck:

    • 简介:Cppcheck 是一个开源的 C/C++ 代码静态分析工具。
    • 特点:能够检查空指针解引用、内存泄漏、数组下标越界、无效的指针操作等问题。
    • 使用:支持命令行和 GUI 版本,可以方便地集成到 CI/CD 流程中。
  3. PVS-Studio:

    • 简介:PVS-Studio 是一个商业的静态代码分析工具,专门用于 C/C++ 代码。
    • 特点:能够检查内存错误、未定义行为、并发问题等。
    • 使用:提供了强大的分析功能和报告,支持多种集成方式,如 IDE 插件、命令行和 CI/CD 集成。
  4. Coverity:

    • 简介:Coverity 是一款商业的静态代码分析工具,支持多种编程语言,包括 C/C++。
    • 特点:能够检查内存泄漏、资源泄漏、并发问题等。
    • 使用:适合大型项目,提供详细的问题报告和高级配置选项。
  5. SonarQube:

    • 简介:SonarQube 是一个开源的代码质量管理平台,支持多种编程语言,包括 C/C++。
    • 特点:静态代码分析、代码复杂度、单元测试覆盖率等功能。
    • 使用:可以通过插件或者集成到 CI/CD 流程中使用,提供详细的报告和问题跟踪。
  6. Valgrind:

    • 简介:Valgrind 是一个强大的内存检查工具,主要用于检测内存泄漏、内存访问错误等问题。
    • 特点:不同于传统的静态分析工具,Valgrind 是动态的内存分析工具。
    • 使用:可以检查 C/C++ 代码的内存问题,但需要在运行时进行分析,因此适用于特定的测试场景。

这些工具可以根据不同的需求和项目特点选择使用,有些是开源的,有些是商业的,提供了不同层次的代码检测和问题分析功能。当然你也可以自己写一个,今天我来介绍下如何使用 Clang 库开发一个 C++ 静态分析工具,以检测代码中的互斥锁使用情况。

准备工作

安装 Clang

首先,我们需要安装 Clang。Clang 是一个 C/C++/Objective-C 编译器,也提供了一些用于代码分析的库。在 Ubuntu 上,你可以使用以下命令安装 Clang:

sudo apt-get install clang libclang-dev

在其他操作系统上,请参考 Clang 的官方文档进行安装。

安装 Python 绑定

我们将使用 Python 编写我们的静态分析工具。为了在 Python 中使用 Clang 库,我们需要安装 Python 绑定。你可以使用 pip 进行安装:

pip install clang

Clang 的架构

在开始编写我们的工具之前,让我们先了解一下 Clang 的架构。

抽象语法树 (AST)

Clang 使用抽象语法树 (Abstract Syntax Tree, AST) 来表示源代码的结构。AST 是一种树形数据结构,其中每个节点表示源代码中的一个语法结构,如声明、语句、表达式等。通过遍历 AST,我们可以分析代码的结构并提取我们感兴趣的信息。

光标 (Cursor)

在 Clang 的术语中,AST 中的每个节点都由一个光标 (Cursor) 表示。光标提供了访问节点信息的接口,如节点的类型、拼写、位置等。通过遍历光标,我们可以遍历整个 AST。

开发静态分析工具

现在,让我们开始编写我们的静态分析工具。我们将分步骤进行介绍。

第一步:创建 Clang 索引

首先,我们需要创建一个 Clang 索引。索引是一个用于管理 AST 的对象。我们可以使用 clang.cindex.Index.create() 函数创建一个索引:

import clang.cindexindex = clang.cindex.Index.create()

第二步:解析源文件

接下来,我们需要解析我们的源文件以获取其 AST。我们可以使用 index.parse() 函数来做到这一点:

tu = index.parse('example.cpp')

这里,tu 是一个翻译单元 (Translation Unit) 对象,表示解析后的源文件。

第三步:遍历 AST

现在我们有了 AST,我们可以开始遍历它。我们可以使用光标的 get_children() 方法获取其子节点:

def traverse(node):for child in node.get_children():# 处理子节点traverse(child)traverse(tu.cursor)

这是一个简单的递归遍历 AST 的函数。对于每个节点,我们对其子节点递归调用 traverse() 函数。

第四步:识别互斥锁

在我们的例子中,我们想要识别代码中使用的互斥锁。在 C++ 中,互斥锁通常是 std::mutex, std::recursive_mutex, std::timed_mutex, std::shared_mutex, std::shared_timed_mutex 等类型的变量。

我们可以通过检查节点的类型来识别这些互斥锁:

def is_lock_type(type_cursor):if type_cursor.kind == clang.cindex.TypeKind.TYPEDEF:type_cursor = type_cursor.get_canonical()if type_cursor.spelling in ['std::mutex', 'std::recursive_mutex', 'std::timed_mutex', 'std::shared_mutex', 'std::shared_timed_mutex']:return Truereturn False

这个函数检查给定的类型是否是互斥锁类型。如果类型是一个 typedef,我们使用 get_canonical() 方法获取其原始类型。然后,我们检查类型的拼写是否匹配已知的互斥锁类型。

第五步:提取互斥锁信息

当我们识别出一个互斥锁时,我们可以提取关于它的各种信息,如其名称、类型、位置等:

def process_lock(node, file_path):lock_info = f"Lock:\n  Name: {node.spelling}\n  Type: {node.type.spelling}\n  File: {file_path}\n  Line: {node.location.line}\n  Column: {node.location.column}\n  Is Member: {node.semantic_parent.kind == clang.cindex.CursorKind.CLASS_DECL}\n"print(lock_info)

这个函数提取互斥锁的各种信息并打印出来。

第六步:识别互斥锁的使用

除了识别互斥锁的定义,我们还想知道它们在哪里被使用。我们可以通过查找对互斥锁方法的调用来做到这一点:

def process_lock_usage(node, lock_names, file_path):if node.kind == clang.cindex.CursorKind.CALL_EXPR:if node.spelling in ['lock', 'try_lock', 'unlock', 'lock_shared', 'try_lock_shared', 'unlock_shared']:for arg in node.get_arguments():if arg.kind == clang.cindex.CursorKind.MEMBER_REF_EXPR or arg.kind == clang.cindex.CursorKind.DECL_REF_EXPR:if arg.referenced.spelling in lock_names:function = node.semantic_parent.spelling if node.semantic_parent else "global scope"usage_info = f"Lock Operation:\n  Name: {arg.referenced.spelling}\n  Operation: {node.spelling}\n  File: {file_path}\n  Line: {node.location.line}\n  Column: {node.location.column}\n  Function: {function}\n  Is Member Function: {node.semantic_parent.kind == clang.cindex.CursorKind.CXX_METHOD}\n"print(usage_info)

这个函数检查每个函数调用表达式。如果调用的是 lock, try_lock, unlock, lock_shared, try_lock_shared, unlock_shared 等方法,我们进一步检查参数。如果参数是一个我们识别出的互斥锁,我们提取关于这个使用的信息并打印出来。

第七步:整合

现在我们有了识别互斥锁定义和使用的函数,我们可以将它们整合到我们的 AST 遍历中:

def traverse(node, file_path, lock_names):if node.kind == clang.cindex.CursorKind.VAR_DECL or node.kind == clang.cindex.CursorKind.FIELD_DECL:if is_lock_type(node.type):process_lock(node, file_path)lock_names.add(node.spelling)process_lock_usage(node, lock_names, file_path)for child in node.get_children():traverse(child, file_path, lock_names)lock_names = set()
traverse(tu.cursor, 'example.cpp', lock_names)

在这个版本的 traverse() 函数中,我们首先检查节点是否是一个互斥锁定义。如果是,我们处理它并将其名称添加到 lock_names 集合中。然后,我们检查节点是否是一个互斥锁使用。最后,我们递归处理子节点。

处理复杂情况

上面的代码展示了静态分析的基本思路。但在实际的 C++ 代码中,情况可能会更加复杂。下面我们讨论一些常见的复杂情况以及如何处理它们。

类型定义和别名

C++ 中经常使用 typedefusing 来定义类型别名。当我们检查一个类型是否是互斥锁时,我们需要考虑这一点:

def is_lock_type(type_cursor):if type_cursor.kind == clang.cindex.TypeKind.TYPEDEF:type_cursor = type_cursor.get_canonical()# 检查类型是否是互斥锁

在这里,如果类型是一个 typedef,我们使用 get_canonical() 方法获取其原始类型,然后再进行检查。

模板类型

C++ 中的 std::lock_guard, std::unique_lock, std::shared_lock 等类型是模板类型。它们的完整类型取决于模板参数:

def is_lock_type(type_cursor):if type_cursor.kind == clang.cindex.TypeKind.RECORD:parent = type_cursor.get_declaration()if parent.kind == clang.cindex.CursorKind.CLASS_TEMPLATE:if parent.spelling in ['std::lock_guard', 'std::unique_lock', 'std::shared_lock']:return True

这里,如果类型是一个记录类型(通常表示类或结构体),我们检查其声明。如果声明是一个类模板,并且其拼写匹配已知的锁模板类型,我们就认为它是一个互斥锁类型。

预处理器指令

C/C++ 代码通常包含许多预处理器指令,如 #include, #ifdef 等。这些指令可能会影响代码的解析。Clang 提供了一些处理预处理器指令的选项:

tu = index.parse('example.cpp', args=['-E', '-std=c++11'])

在这里,我们使用 -E 选项来进行预处理,使用 -std=c++11 选项来指定 C++ 标准。你可能需要根据你的具体情况调整这些选项。

错误处理

解析和分析 C++ 代码并不总是一帆风顺的。我们的工具应该能够优雅地处理错误:

try:tu = index.parse('example.cpp')
except clang.cindex.TranslationUnitLoadError as e:print(f"Failed to parse file: {e}")return

在这里,我们使用 try-except 块来捕获解析错误。如果发生错误,我们打印错误信息并返回,而不是让程序崩溃。

性能优化

静态分析可能是一项计算密集型任务,特别是对于大型代码库。以下是一些可能的优化策略:

  1. 使用 clang.cindex.TranslationUnit.PARSE_SKIP_FUNCTION_BODIES 选项跳过函数体的解析,如果你只对函数签名感兴趣的话。

  2. 使用 clang.cindex.TranslationUnit.PARSE_CACHE_COMPLETION_RESULTS 选项缓存完成结果,以加速后续的解析。

  3. 并行处理多个文件。

  4. 使用增量编译,只重新解析修改过的文件。

请注意,优化总是需要根据具体情况进行权衡。例如,跳过函数体的解析可能会加速解析过程,但也可能导致我们丢失一些信息。

基于 LLVM 的 C++ 静态分析工具开发教程

简介

静态代码分析是一种强大的技术,可以帮助开发者在编译之前发现代码中的潜在问题。通过分析代码的结构和语义,静态分析工具可以发现诸如空指针解引用、资源泄漏、竞态条件等问题。本教程将介绍如何使用 LLVM 库开发一个 C++ 静态分析工具。

LLVM 简介

LLVM 是一个强大的编译器基础设施,广泛用于开发编译器、优化器、静态分析器等工具。LLVM 采用模块化的架构,由一系列可重用的库组成,这使得它非常适合开发自定义的代码分析工具。

环境设置

在开始之前,我们需要设置开发环境。

安装 LLVM

首先,我们需要安装 LLVM。在 Ubuntu 上,你可以使用以下命令安装 LLVM:

sudo apt-get install llvm

在其他操作系统上,请参考 LLVM 的官方文档进行安装。

创建项目

接下来,让我们创建一个新的 C++ 项目:

mkdir StaticAnalyzer
cd StaticAnalyzer

在这个目录下,创建一个名为 CMakeLists.txt 的文件,内容如下:

cmake_minimum_required(VERSION 3.10)
project(StaticAnalyzer)set(CMAKE_CXX_STANDARD 17)find_package(LLVM REQUIRED CONFIG)
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})add_executable(StaticAnalyzer StaticAnalyzer.cpp)llvm_map_components_to_libnames(llvm_libs support core irreader)
target_link_libraries(StaticAnalyzer ${llvm_libs})

这个 CMake 文件定义了我们的项目,并链接了必要的 LLVM 库。

LLVM 的架构

在开始编写我们的工具之前,让我们先了解一下 LLVM 的架构。

中间表示 (IR)

LLVM 使用一种叫做中间表示 (Intermediate Representation, IR) 的方式来表示代码。IR 是一种低级的、与目标无关的表示,类似于汇编语言。LLVM 提供了一组丰富的 API 来创建、操作和分析 IR。

Pass 框架

LLVM 的另一个重要概念是 Pass。Pass 是对 IR 进行操作的模块化单元。例如,优化器中的每个优化都是一个 Pass。LLVM 提供了一个 Pass 框架,用于管理 Pass 的执行。

开发静态分析工具

现在,让我们开始编写我们的静态分析工具。我们将分步骤进行介绍。

第一步:解析源文件

首先,我们需要解析我们的源文件以获取其 IR。我们可以使用 LLVM 的 parseIRFile 函数来做到这一点:

#include "llvm/IRReader/IRReader.h"
#include "llvm/Support/SourceMgr.h"using namespace llvm;SMDiagnostic Err;
LLVMContext Context;
std::unique_ptr<Module> Mod = parseIRFile("example.ll", Err, Context);
if (!Mod) {Err.print("StaticAnalyzer", errs());return 1;
}

在这里,我们使用 parseIRFile 函数解析名为 example.ll 的 LLVM IR 文件。如果解析成功,我们就获得了一个表示这个模块的 Module 对象。

第二步:编写分析 Pass

接下来,我们需要编写我们的分析 Pass。Pass 是继承自 FunctionPass 的类:

#include "llvm/Pass.h"using namespace llvm;struct StaticAnalyzerPass : public FunctionPass {static char ID;StaticAnalyzerPass() : FunctionPass(ID) {}bool runOnFunction(Function &F) override {// 在这里进行分析return false;}
};char StaticAnalyzerPass::ID = 0;

runOnFunction 方法中,我们可以访问当前的函数 F。这是我们执行分析的地方。

第三步:注册 Pass

为了让 LLVM 知道我们的 Pass,我们需要注册它:

static RegisterPass<StaticAnalyzerPass> X("static-analyzer", "Static Analyzer Pass");

这行代码注册了我们的 Pass,给它起名为 “static-analyzer”。

第四步:运行 Pass

现在,我们可以运行我们的 Pass 了:

legacy::PassManager PM;
PM.add(new StaticAnalyzerPass());
PM.run(*Mod);

这里,我们创建一个 PassManager,添加我们的 Pass,然后在我们的模块上运行它。

第五步:分析代码

最后,我们可以在 runOnFunction 方法中进行实际的代码分析。例如,我们可以检查每个函数中的每个指令:

bool runOnFunction(Function &F) override {for (auto &BB : F) {for (auto &I : BB) {// 分析指令 I}}return false;
}

在这里,我们使用嵌套的范围 for 循环遍历函数中的每个基本块(BB)和每个指令(I)。我们可以检查指令的类型,分析其操作数,等等。

处理复杂情况

实际的 C++ 代码可能非常复杂,包含各种语言特性和库。以下是一些处理复杂情况的建议:

处理标准库

C++ 标准库广泛使用了模板和内联函数。这可能会让 IR 变得非常复杂。一种可能的策略是提供一组预定义的规则来处理标准库中的常见模式。

处理虚函数

虚函数调用在 IR 中通常表示为间接调用。你可能需要进行额外的分析来确定可能的调用目标。LLVM 提供了一些用于此目的的分析 Pass,如 CallGraph

处理异常

异常处理会引入复杂的控制流。你可能需要特殊处理 invoke 指令和 landingpad 块。

性能优化

静态分析可能非常耗时,特别是对于大型的代码库。以下是一些可能的优化策略:

  1. 使用增量分析,只分析修改过的函数。
  2. 并行运行多个分析 Pass。
  3. 使用更高级的数据结构,如 BDD 或 SAT solver,来加速分析。

请注意,优化总是需要根据具体情况进行权衡。例如,使用更高级的数据结构可能会加速分析,但也可能增加实现的复杂性。

结论

在本教程中,我们学习了如何使用 Clang 库开发一个 C++ 静态分析工具。我们介绍了 Clang 的基本架构,如抽象语法树 (AST) 和光标 (Cursor),并展示了如何使用这些概念来分析 C++ 代码。也学习了如何使用 LLVM 库开发一个 C++ 静态分析工具。我们介绍了 LLVM 的基本架构,如中间表示 (IR) 和 Pass 框架,并展示了如何使用这些概念来分析 C++ 代码。我们还讨论了一些实际开发中可能遇到的复杂情况以及如何处理它们。

开发一个全面的静态分析工具是一项具有挑战性的任务,需要深入理解编程语言的语法和语义,以及编译器的工作原理。LLVM 提供了一个强大的基础设施,但要充分利用它仍然需要大量的工作。

随着你对 Clang 和静态分析的理解不断深入,你将能够开发出更加复杂和精巧的工具。

相关文章:

基于 Clang和LLVM 的 C++ 代码静态分析工具开发教程

基于 Clang和LLVM 的 C 代码静态分析工具开发教程 简介 静态代码分析是一种在不实际运行程序的情况下对源代码进行分析的技术。它可以帮助开发者在编译之前发现潜在的错误、安全漏洞、性能问题等。 在 C 开发中&#xff0c;有几种常用的静态代码分析工具&#xff0c;它们可以…...

Mathtype与word字号对照+Mathtype与word字号对照

字体大小对照表如下 初号44pt 小初36pt 一号26pt 小一24pt 二号22pt 小二18pt 三号16pt 小三15pt 四号14pt 小四12pt 五号10.5pt 小五9pt 六号7.5pt 小六6.5pt 七号5.5pt 八号5pt 1 保存12pt文件 首选选择第一个公式&#xff0c;将其大小改为12pt 然后依次选择 “预置”—…...

PHP 8.4有哪些新功能值得关注

属性钩子&#xff08;Property Hooks&#xff09; 允许开发者为每个属性定义自己的get和set钩子&#xff0c;以在属性访问前后添加自定义逻辑。属性钩子通过__get()和__set()方法实现&#xff0c;类似于其他编程语言&#xff08;如Kotlin、C#和Swift&#xff09;中的属性访问器…...

PyCharm新手入门

前言 在之前《Python集成开发工具的选择》一文中介绍了python初学者可以使用Jupyter Notebook&#xff0c;Jupyter Notebook简单易用&#xff0c;可以用来练习代码编写&#xff0c;但是实际生产开发环境使用这个工具是远远不够用的&#xff0c;因为实际软件开发中需要软件调试…...

[Linux] 系统管理

全局配置文件 用户个性化配置 配置文件的种类 alias命令和unalias命令 进程管理 进程表...

Xcode无法使用设备:Failed to prepare the device for development

问题&#xff1a; Xcode无法使用设备开发&#xff0c;失败报错如下&#xff1a; Failed to prepare the device for development. This operation can fail if the version of the OS on the device is incompatible with the installed version of Xcode. You may also need…...

AWS无服务器 应用程序开发—第十二章 AWS Step Functions

AWS Step Functions 是一种服务,用于协调和管理分布式应用程序中的多个 AWS 服务和 Lambda 函数。它通过创建有状态的工作流来简化和自动化应用程序的各种工作流程,使得复杂的业务逻辑可以以可管理和可调试的方式实现。 主要功能和特点: 状态机定义: 使用 JSON 或 Amazo…...

Linux tcpdump详解

目录 前言&#xff1a;BPF伯克利包过滤器介绍1.BPF语法&#xff08;tcpdump语法&#xff09;2.逻辑运算符3.常用的原子条件1. 协议相关的原子条件2. 地址相关的原子条件3. 端口相关的原子条件4. 网络层和链路层(mac地址&#xff09;原子条件5. 广播和多播6. VLAN 相关的原子条件…...

vue2实现打印功能(vue-print-nb的实现)

实现效果&#xff1a; 引入插件 npm install vue-print-nb --save import Print from vue-print-nb Vue.use(Print) <div ref"printTest" id"printTest"><div style"text-align: center; page-break-after: always"><div style…...

某全国增值税发票查验平台 接口JS逆向

注意&#xff0c;本文只提供学习的思路&#xff0c;严禁违反法律以及破坏信息系统等行为&#xff0c;本文只提供思路 本文的验证码网址如下&#xff0c;使用base64解码获得 aHR0cHM6Ly9pbnYtdmVyaS5jaGluYXRheC5nb3YuY24v 这个平台功能没什么好说的&#xff0c;就是发票查验&am…...

前端练习小项目——视觉冲击卡片

前言&#xff1a; 前言&#xff1a;在学习完HTML和CSS之后&#xff0c;我们就可以开始做一些小项目了&#xff0c;本篇文章所讲的小项目为——视觉冲击卡片 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客 先让我们看一下效果&a…...

从0到1:手动测试迈向自动化——手机web应用的自动化测试工具

引言&#xff1a; 在当今移动互联网时代&#xff0c;手机web应用已经成为人们生活中不可或缺的一部分。为了保证手机web应用的质量和稳定性&#xff0c;自动化测试工具变得十分重要。本文将介绍手机web应用自动化测试工具的选择和使用&#xff0c;提供一份超详细且规范的指南&a…...

磁盘未格式化:深度解析、恢复策略与预防措施

一、磁盘未格式化的定义与现象 在计算机存储领域&#xff0c;磁盘未格式化通常指的是磁盘分区或整个磁盘的文件系统信息出现丢失或损坏的情况&#xff0c;导致操作系统无法正确读取和识别磁盘上的数据。当尝试访问这样的磁盘时&#xff0c;系统往往会弹出一个警告框&#xff0…...

Qwen2在Java项目中如何实现优雅的Function_Call工具调用

在当今AI技术飞速发展的背景下&#xff0c;大语言模型如Qwen2和GLM-4凭借其强大的语言处理能力&#xff0c;在诸多领域展现出了巨大的潜力。然而&#xff0c;大模型并非全知全能&#xff0c;它们在处理特定任务时&#xff0c;尤其是在需要与外部系统交互或执行具体功能时&#…...

【SPIE独立出版 | 往届均已完成EI检索】2024云计算、性能计算与深度学习国际学术会议(CCPCDL 2024)

2024云计算、性能计算与深度学习国际学术会议(CCPCDL 2024) 2024 International conference on Cloud Computing, Performance Computing and Deep Learning *CCPCDL往届均已完成EI检索&#xff0c;最快会后4个半月完成&#xff01; 一、重要信息 大会官网&#xff1a;www…...

MYSQL 四、mysql进阶 4(索引的数据结构)

一、为什么使用索引 以及 索引的优缺点 1.为什么使用索引 索引是存储引擎用于快速找到数据记录的一种数据结构&#xff0c;就好比一本教科书的目录部分&#xff0c;通过目录中找到对应文章的页码&#xff0c;便可快速定位到需要的文章。Mysql中也是一样的道理&#xff0c;进行数…...

360vr党建线上主题展立体化呈现企业的文化理念和品牌形象

在现代科技的引领下&#xff0c;艺术与VR虚拟现实技术相融合必将成为趋势&#xff0c;深圳VR公司华锐视点荣幸地推出VR艺术品虚拟展厅&#xff0c;为您带来前所未有的艺术观赏体验。体验者足不出户即可置身于一个充满创意与灵感的虚拟艺术空间。 我们深入了解每一位客户的需求与…...

docker通过容器id查看运行命令;Portainer监控管理docker容器

1、docker通过容器id查看运行命令 参考&#xff1a;https://blog.csdn.net/a772304419/article/details/138732138 docker inspect 运行镜像id“Cmd”: [ “–model”, “/qwen-7b”, “–port”, “10860”, “–max-model-len”, “4096”, “–trust-remote-code”, “–t…...

XMind 2024软件最新版下载及详细安装教程

​人所共知的是XMind 在公司和教育领域都有很广泛的应用&#xff0c;在公司中它能够用来进行会议管理、项目管理、信息管理、计划和XMind 被认为是一种新一代演示软件的模式。也就是说XMind不仅能够绘制思维导图&#xff0c;还能够绘制鱼骨图、二维图、树形图、逻辑图、组织结构…...

代码随想录算法训练营第四十四天 | 322. 零钱兑换、279.完全平方数、139.单词拆分、多重背包理论基础、背包问题总结

322. 零钱兑换 题目链接&#xff1a;https://leetcode.cn/problems/coin-change/ 文档讲解&#xff1a;https://programmercarl.com/0322.%E9%9B%B6%E9%92%B1%E5%85%91%E6%8D%A2.html 视频讲解&#xff1a;https://www.bilibili.com/video/BV14K411R7yv/ 思路 确定dp数组以及下…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)

考察一般的三次多项式&#xff0c;以r为参数&#xff1a; p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]&#xff1b; 此多项式的根为&#xff1a; 尽管看起来这个多项式是特殊的&#xff0c;其实一般的三次多项式都是可以通过线性变换化为这个形式…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...

LabVIEW双光子成像系统技术

双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制&#xff0c;展现出显著的技术优势&#xff1a; 深层组织穿透能力&#xff1a;适用于活体组织深度成像 高分辨率观测性能&#xff1a;满足微观结构的精细研究需求 低光毒性特点&#xff1a;减少对样本的损伤…...