当前位置: 首页 > 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数组以及下…...

开源AGV调度系统OpenTCS中的路由器(router)详解

OpenTCS中的任务分派器router详解 1. 引言2. 路由器(router)2.1 代价计算函数&#xff08;Cost functions&#xff09;2.2 2.1 Routing groups2.1 默认的停车位置选择2.2 可选停车位置属性2.3 默认的充电位置选择2.4 即时运输订单分配 3. 默认任务分派器的配置项4. 参考资料与源…...

关于下载 IDEA、WebStorm 的一些心得感想

背景 实习第一天的时候&#xff0c;睿哥便吩咐我下载一些软件&#xff0c;这些软件以后在写项目的时候会用到&#xff0c;他叫我先装IDEA,WebStorm&#xff0c;微信开发者工具&#xff0c;git&#xff0c;还有Navicat。 这些软件能够被我们正常使用&#xff0c;无非就通过三步…...

C#使用Scoket实现服务器和客户端互发信息

20240616 By wdhuag 目录 前言&#xff1a; 参考&#xff1a; 一、服务器端&#xff1a; 1、服务器端口绑定&#xff1a; 2、服务器关闭&#xff1a; 二、客户端&#xff1a; 1、客户端连接&#xff1a; 2、客户端断开&#xff1a; 三、通讯&#xff1a; 1、接收信…...

【经验分享】SpringCloud + MyBatis Plus 配置 MySQL,TDengine 双数据源

概述 因为项目中采集工厂中的设备码点的数据量比较大,需要集成TDengine时序数据库,所以需要设置双数据源 操作步骤 导入依赖 <!-- 多数据源支持 --><dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-s…...

Pycharm 忽略文件

安装 .ignore插件 规则示例 罗列一些常遇到.getignore忽略规则的使用示例&#xff1a; 1. 在已忽略文件夹中不忽略指定文件夹&#xff1a; /libs/* !/libs/extend/ 2. 在已忽略文件夹中不忽略指定文件 /libs/* !/libs/extend/fastjson.jar 3.只忽略libs目录&#xf…...

爬虫学习。。。。

爬虫的概念&#xff1a; 爬虫是一种自动化信息采集程序或脚本&#xff0c;用于从互联网上抓取信息。 它通过模拟浏览器请求站点的行为&#xff0c;获取资源后分析并提取有用数据&#xff0c;这些数据可以是HTML代码、JSON数据或二进制数据&#xff08;如图片、视频&#xff09…...

美国铁路客运巨头Amtrak泄漏旅客数据,数据销毁 硬盘销毁 文件销毁

旅客的Guest Rewards常旅客积分账户的个人信息被大量窃取。 美国国家客运铁路公司&#xff08;Amtrak&#xff09;近日披露了一起数据泄露事件&#xff0c;旅客的Guest Rewards常旅客积分账户的个人信息被大量窃取。 根据Amtrak向马萨诸塞州提交的泄露通知&#xff0c;5月15日…...

LabVIEW与Matlab联合编程的途径及比较

​ LabVIEW和Matlab联合编程可以通过多种途径实现&#xff0c;包括调用Matlab脚本节点、使用LabVIEW MathScript RT模块、利用ActiveX和COM接口&#xff0c;以及通过文件读写实现数据交换。每种方法都有其独特的优势和适用场景。本文将详细比较这些方法&#xff0c;帮助开发者…...

秋招突击——6/16——复习{(单调队列优化DP)——最大子序和,背包模型——宠物小精灵收服问题}——新作{二叉树的后序遍历}

文章目录 引言复习&#xff08;单调队列优化DP&#xff09;——最大子序和单调队列的基本实现思路——求可移动窗口中的最值总结 背包模型——宠物小精灵收服问题思路分析参考思路分析 新作二叉树的后续遍历加指针调换 总结 引言 复习 &#xff08;单调队列优化DP&#xff09…...

SAR动目标检测系列:【4】动目标二维速度估计

在三大类杂波抑制技术(ATI、DPCA和STAP)中&#xff0c;STAP技术利用杂波与动目标在二维空时谱的差异&#xff0c;以信噪比最优为准则&#xff0c;对地杂波抑制的同时有效保留动目标后向散射能量&#xff0c;有效提高运动目标的检测概率和动目标信号输出信杂比&#xff0c;提供理…...

网站推广外链/百度网站站长工具

前段时间参加 ESRI用户大会遇到Roger Tomlinson先生&#xff0c;他可是咱GIS行业公认的开山鼻祖。而本人不但求到了他的亲笔签名的书&#xff0c;还有幸和他合了张影&#xff0c;和这位大牛来了个"亲密接触"&#xff0c;也深刻地让我体会到了这位Grandfather&#xf…...

政府网站品牌建设方案/网店推广常用的方法

eft简介 Deft是一个超轻量级高性能O/R mapping数据访问组件&#xff0c;简单易用&#xff0c;几分钟即可上手。 原文地址&#xff1a;/StrangeCity/p/5566471.html Deft包含如下且不限于此的优点&#xff1a; 1、按照Transact-SQL的语法语义风格来设计&#xff0c;只要调用者熟…...

wordpress在线播放avi/最简单的营销方案

会话控制&#xff1a; 因为HTTP协议是无状态的&#xff0c;服务器不知道用户上一次做了什么&#xff0c;这严重阻碍了交互式 web应用程序的实现。HTTP不通过额外的手段&#xff0c;服务器并不知道用户做了什么&#xff0c;为了做 到这一点&#xff0c;就需要使用cookie和sessi…...

东营专业网站建设公司排行/下载百度地图2022最新版官方

概述 这是示例主要用来获取公众号和用户进行文本信息传递时的解析方法 源码 <?phpdefine("ToKEN", "weixin"); //定义一个常量 $wechatObj new wechatCallbackapiTest();//标准模版 if (isset($_GET[echostr])) {//echo $_GET[echostr];$wechatObj-…...

做网站找众展/品牌推广专员

经典励志句子 如果有醒不来的梦&#xff0c;我一定去做如果有醒不来的梦。我一定去做。如果有走不完的路。我一定去走。如果有变不了的爱。我一定去求。如果这辈子失去了你。那么我将连回忆都不再悲伤。有一种相遇可能叫邂逅。可缘于命运。我现在为一能做的就是将回忆永远收藏。…...

82端口做网站/开封seo公司

Vue Springboot MySQL制作搜索引擎&#xff08;三&#xff09;后端用 spring boot 连接数据库和前端接口设计 后端我用 IntelliJ IDEA 软件。 一. 创建项目 file-new-project next next 选择web-spring web&#xff0c;next 后面就是选择文件存储位置&#xff0c;这个按…...