Solidity编码规范汇总篇

本文首发于公众号 【Keegan小钢】


上周,完成了 Solidity 编码规范的视频录制并上传到了 B 站、Youtube 和视频号。总共分为了 6 个小节,在 B 站的合集地址为:

https://space.bilibili.com/60539794/channel/collectiondetail?sid=3780183

为了方便一些更习惯看文章的同学,这两天我又整理出了文字版本,以下就是该 Solidity 编码规范汇总篇。

为什么需要编码规范

我们知道,每一门编码语言都会有各自的编码规范。那为什么需要编码规范呢?我总结出以下几个原因:

  1. 提高代码可读性: 编码规范使得代码结构更加清晰,命名更加直观,逻辑更加明确,这样其他开发者(包括编写代码的开发者自己)可以更容易地理解和维护代码。
  2. 促进团队协作: 在团队开发中,统一的编码规范使得不同开发者编写的代码风格一致,避免了代码风格上的差异导致的混乱和误解,有助于团队成员之间的合作和代码的整合。
  3. 减少错误: 良好的编码规范通常包括一些最佳实践,如变量命名规则、代码注释等。这些规范可以帮助开发者避免常见的错误,提高代码的可靠性和健壮性。
  4. 提高维护性: 当代码风格统一时,维护代码的工作量会显著减少。开发者可以快速理解代码的逻辑,进行修改和扩展,降低维护的成本和难度。
  5. 有助于代码审查: 编码规范使得代码审查变得更加高效,审查者可以专注于代码的逻辑和功能,而不是在代码风格上浪费时间。
  6. 提升代码质量: 统一的编码规范通常伴随着对代码质量的关注,如代码的可扩展性、可测试性等。这些规范有助于编写出高质量的代码。
  7. 遵循行业标准: 编码规范通常是根据行业标准或社区共识制定的,遵循这些规范可以确保代码与其他项目或开源库兼容,促进代码的复用和共享。

总的来说,编码规范对于保证代码的质量、提高开发效率、促进团队合作、降低维护成本等方面都有着重要作用,因此每一门编程语言都需要编码规范,Solidity 作为智能合约的开发语言也不例外。下面就会正式介绍 Solidity 这门语言的编码规范。

文件命名

我们编码的第一步就是需要创建源文件,所以第一步我想从文件命名说起。而关于文件命名这部分的编码规范,我总结下来可以归为四点:

  • 采用大驼峰式命名法
  • 与合约组件名保持一致
  • 每个文件里只定义一个合约组件
  • interface 命名额外添加大写 I 作为前缀

在 solidity 官方文档里,有列出几种不同的命名风格,如下:

  • b (single lowercase letter)
  • B (single uppercase letter)
  • lowercase
  • UPPERCASE
  • UPPER_CASE_WITH_UNDERSCORES
  • CapitalizedWords (or CapWords)
  • mixedCase (differs from CapitalizedWords by initial lowercase character!)

首先,所谓大驼峰式命名法,其实就是 CapitalizedWords 这种风格的命名法,就是由首字母大写的单词组合而成的写法。而 mixedCase 也称为小驼峰式命名法。

合约组件,其实就是 interfacelibrarycontract。文件名要与合约组件名保持一致,就是说要与文件里所定义的 interface/library/contract 的名称一样。比如文件名为 UniswapV3Pool.sol,文件里定义的 contract 名称就是 UniswapV3Pool

每个文件里只定义一个合约组件,就是不要在文件里同时定义多个合约组件。比如,Uniswap 的 v3-core 里,有定义了几个回调类的接口:

  • IUniswapV3FlashCallback
  • IUniswapV3MintCallback
  • IUniswapV3SwapCallback

这三个接口,每个接口里面都只定义了一个函数。从语法上来说,是可以只用一个文件来包含这三个接口的,比如就用 UniswapV3Callback.sol,文件里则大致如下:

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0interface IUniswapV3FlashCallback {...
}interface IUniswapV3MintCallback {...
}interface IUniswapV3SwapCallback {...
}

从语法上来说,这是没有错的。但是,Uniswap 则将其分为了三个文件,如下:

  • IUniswapV3FlashCallback.sol
  • IUniswapV3MintCallback.sol
  • IUniswapV3SwapCallback.sol

这就是每个文件里只定义一个合约组件。

另外,上面这个例子也涉及到了最后一点命名规范,即 interface 命名额外添加大写 I 作为前缀。你可以看到这几个 interface 都是加了大写 I 作为前缀的,而 librarycontract 则不会有这种前缀。遵循这种规范,就可以直接从文件命名区分出哪些是 interface 了。

文件编排

在一个项目中,通常都不会只有少数几个文件,那文件多了,自然是需要做好文件编排了。那关于文件编排这块的规范,我的建议就是,可以将不同类型的多个合约文件归类到不同文件夹下。

比如,存在多个 interface 时,可以将这些 interface 文件统一归类到 interfaces 目录下。而多个 library 文件则可以统一归类到 libraries 目录下。抽象合约则可以放置于 baseutils 目录下。而需要进行部署的主合约则大多直接放在 contractssrc 主目录下。

另外,文件比较多的情况下还可以进一步拆分目录。比如,Uniswap/v3-coreinterfaces 下还再划分出了 callbackpool 两个子目录。

而像 OpenZeppelin 这种库则又不一样,它更多的是按功能模块进行目录分类,不同的合约文件是划分到不同的功能目录下的。在这些功能目录下,大多就没有再根据合约组件类型进行进一步的目录划分。比如,access 目录下就直接放置了 IAccessControl.sol,一来,并没有那么多 interface,二来,目录编排更多是根据功能设置的。但它依然也有一个 interfaces 目录存放了所有的接口文件。

合约声明

接下来,就开始进入文件内部编码的规范了。这一小节,先来总结下合约声明部分。

首先,第一行,我们应该声明 SPDX-License-Identifier,指定要使用的许可证。虽然不声明这个,编译不会报错,但会有警告,为了消除警告,所以我们最好还是把这个声明补上。

第二行,使用 pragma 声明要支持的 solidity 版本。关于这个,我的建议是分情况。如果是 interface、library、抽象合约,建议声明为兼容版本,比如 ^0.8.0。如果是需要部署的主合约则建议声明为固定版本。关于这一点,Uniswap/v4-core 是个很好的示例。其主合约 PoolManagerpragma 声明如下:

pragma solidity 0.8.26

可以看到,它声明为了一个固定的版本 0.8.26

而所有 interface、library、抽象合约全都声明为兼容版本,有的是 ^0.8.0,有的是 ^0.8.24。声明为 ^0.8.24 的通常是因为用到了该版本才开始支持的一些特性。

而关于 import 部分,主要有两个建议:一是建议指定名称导入;二是要 import 的第三方合约存在多个版本时,建议指定版本号。

直接看下面这几个例子:

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20} from "@openzeppelin/contracts@0.5.2/token/ERC20/ERC20.sol";

上面三种 import 的写法,第一种是很多人(也包括我)之前最常用的写法,就是直接导入整个文件。第二种增加了要导入的具体合约名称,这就是前面说的,建议指定名称导入。第三种,在 contracts 后面增加了 @0.5.2 的声明来制定要导入的具体版本号。

这第三种写法就是我们最推荐的一种导入方式。因为 OpenZeppelin 已经发布了多个不同版本的库。

不过,如果要导入的第三方合约并不存在多个不同版本的情况下,则没必要增加版本声明了。比如,@uniswap/v3-core 只有一个版本的库,那导入该库的合约时就没必要指定版本了,如下即可:

import {IUniswapV3Pool} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";

interface

interface 是合约交互的接口,也是前端和后端交互时用到的 abi 的来源,所以我们编写实际代码时,通常都会先定义 interface。这一节就来讲讲关于 interface 编码时有哪些编码规范。

首先,从代码结构上,建议以下面这种顺序进行编排:

  • Errors
  • Events
  • Enums
  • Structs
  • Functions

首先定义 Errorserror 类型是在 0.8.4 才开始支持的,所以,如果 interface 里面有定义 error,建议声明 pragma 时至少指定为 ^0.8.4。而声明 error 时,命名建议采用大驼峰式命名法,如果有带参数,则参数采用小驼峰式命名法。如下示例:

error ZeroAddress();
error InvalidAddress(address addr);

接着,定义 Events。所定义的事件名采用大驼峰式命名法,事件参数同样也是采用小驼峰式命名法。另外,如果参数较多的情况下,建议每个参数单独一行。如下示例:

event Transfer(address indexed from, address indexed to, uint value);
event TransferBatch(address indexed operator,address indexed from,address indexed to,uint256[] ids,uint256[] values
);

其实,官方文档里也有说明,建议每一行代码不要超过 120 字符,所以太长的情况下就要用换行的方式进行编排。在 Solidity,每个参数单独一行是很常见的一种编码格式。

紧接着,如果有的话则可以定义 Enums,即枚举类型。命名也是采用大驼峰式命名法,如下示例:

enum OrderStatus {Pending,Shipped,Delivered,Canceled
}

之后,则可以定义 Structs,即结构体。结构体的名称采用大驼峰式命名法,里面的字段则采用小驼峰式命名法,如下示例:

struct PopulatedTick {int24 tick;int128 liquidityNet;uint128 liquidityGross;
}

然后,就可以定义 Functions 了。但函数还可以再进行细分,可以根据下面的顺序进行声明:

  • External functions with payable
  • External functions
  • External functions with view
  • External functions with pure

就是说,我们在 interface 里定义多个函数的时候,建议按照这个顺序对这些函数进行编排。以下是几个示例:

// external function with payable
function burn(uint256 tokenId) external payable;
function createAndInitializePoolIfNecessary(address token0,address token1,uint24 fee,uint160 sqrtPriceX96
) external payable returns (address pool);// external function
function mint(address recipient,int24 tickLower,int24 tickUpper,uint128 amount,bytes calldata data
) external returns (uint256 amount0, uint256 amount1);// external function with view
function getPopulatedTicksInWord(address pool, int16 tickBitmapIndex)externalviewreturns (PolulatedTick[] memory populatedTicks);function ticks(int24 tick)externalviewreturns (uint128 liquidityGross,int128 liquidityNet,uint256 feeGrowthOutside0X128,uint256 feeGrowthOutside1X128,int56 tickCumulativeOutside,uint160 secondsPerLiquidityOutsideX128,uint32 secondsOutside,bool initialized);// external function with pure
function mulDiv(uint a, uint b, uint c) external pure returns (uint d);

除了顺序之外,这里面还有一些细节需要说一说。

首先,可以看到,不管是函数名还是参数名,都是采用了小驼峰式命名法。

其次,还可以看到有不一样的换行格式。createAndInitializePoolIfNecessarymint 这两个函数都是参数各自一行。getPopulatedTicksInWord 则变成了函数修饰符和 returns 语句各自一行,这也是一种换行的写法。最后,ticks 函数的返回参数列表又拆分为每个返回参数单独一行,当返回参数较多的时候这也是常用的写法。

library

接着,来聊聊关于 library 的用法。

library 里无法定义状态变量,所以 library 其实是无状态的。

从语法上来说,library 可以定义不同可见性的函数,但实际应用中,通常只定义 internalprivate 的函数,很少会定义外部可见的 externalpublic 函数。如果一个 library 里有定义了外部可见的函数,那部署的时候也会和只有内部函数的 library 不一样。当然,这是另一个话题了。

library 也可以定义常量和 errors。有些当纯只定义了常量的 library,比如 Uniswap/v4-periphery 里有一个 library 如下:

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;/// @title Action Constants
/// @notice Common constants used in actions
/// @dev Constants are gas efficient alternatives to their literal values
library ActionConstants {/// @notice used to signal that an action should use the input value of the open delta on the pool manager/// or of the balance that the contract holdsuint128 internal constant OPEN_DELTA = 0;/// @notice used to signal that an action should use the contract's entire balance of a currency/// This value is equivalent to 1<<255, i.e. a singular 1 in the most significant bit.uint256 internal constant CONTRACT_BALANCE = 0x8000000000000000000000000000000000000000000000000000000000000000;/// @notice used to signal that the recipient of an action should be the msgSenderaddress internal constant MSG_SENDER = address(1);/// @notice used to signal that the recipient of an action should be the address(this)address internal constant ADDRESS_THIS = address(2);
}

可以看到,这个 ActionConstants 就只是定义了 4 个常量而已。

library 使用场景最多的其实是对特定类型的扩展功能进行封装。UniswapV3Pool 代码体里一开始就声明了一堆 using for,如下所示:

contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall {using LowGasSafeMath for uint256;using LowGasSafeMath for int256;using SafeCast for uint256;using SafeCast for int256;using Tick for mapping(int24 => Tick.Info);using TickBitmap for mapping(int16 => uint256);using Position for mapping(bytes32 => Position.Info);using Position for Position.Info;using Oracle for Oracle.Observation[65535];...
}

这些 using 后面的 libraries 封装的函数大多就是对 for 类型的功能扩展。比如,LowGasSafeMath 可以理解为就是对 uint256 类型的功能扩展,其内定义的函数第一个参数都是 uint256 类型的。LowGasSafeMath 里有定义了一个 add 函数,如下:

function add(uint256 x, uint256 y) internal pure returns (uint256 z) {require((z = x + y) >= x);
}

UniswapV3Pool 使用到 LowGasSafeMathadd 函数的地方其实不少,比如下面这个:

uint256 balance0Before = balance0();
require(balance0Before.add(uint256(amount0)) <= balance0(), 'IIA');

其中,balance0Before.add(uint256(amount0)) 其实就是调用了 LowGasSafeMathadd 函数。而且,通过这种语法糖的包装调用,不就等于变成了uint256 这个类型本身的扩展函数了,所以才说 library 可以理解为是对特定类型的功能扩展。

最后,虽然 library 本身没法直接访问状态变量,但状态变量是可以通过函数传参的方式间接被 library 函数所访问到的,只要将函数参数声明为 storage 即可。比如 library Position,声明了 getupdate 函数,这两个函数的第一个参数都带了 storage 修饰,其核心代码如下:

function get(mapping(bytes32 => Info) storage self,address owner,int24 tickLower,int24 tickUpper
) internal view returns (Position.Info storage position) {position = self[keccak256(abi.encodePacked(owner, tickLower, tickUpper))];
}function update(Info storage self,int128 liquidityDelta,uint256 feeGrowthInside0X128,uint256 feeGrowthInside1X128
) internal {...// update the positionif (liquidityDelta != 0) self.liquidity = liquidityNext;self.feeGrowthInside0LastX128 = feeGrowthInside0X128;self.feeGrowthInside1LastX128 = feeGrowthInside1X128;if (tokensOwed0 > 0 || tokensOwed1 > 0) {// overflow is acceptable, have to withdraw before you hit type(uint128).max feesself.tokensOwed0 += tokensOwed0;self.tokensOwed1 += tokensOwed1;}
}

get 函数的 self 参数就是 storage 的,而且数据类型是 mapping 的,意味着传入的这个参数是个状态变量,而返回值也是 storage 的,所以返回的 position 其实也是状态变量。

update 函数的 self 参数也一样是 storage 的,也同样是一个状态变量,在代码里对 self 进行了修改,也是会直接反映到该状态变量在链上的状态变更。

contract

下面就来聊聊 contract 方面的编码规范,主要讲代码结构,一般建议按照以下顺序进行编排:

  • using for
  • 常量
  • 状态变量
  • Events
  • Errors
  • Modifiers
  • Functions

首先,如果需要用到库,那就先用 using for 声明所使用的库。

接着,定义常量。常量还分为 public 常量和 private 常量,一般建议先声明 public 的再声明 private 的。另外,常量命名采用带下划线的全字母大写法,像这样:UPPER_CASE_WITH_UNDERSCORES

声明完常量之后,紧接着就可以声明状态变量。状态变量也是先声明 public 的,然后再声明 private 的。命名则通常采用小驼峰式命名法。另外,private 的状态变量通常还会再添加前下划线,以和 public 的变量区分开来。如下示例:

// public state variable
uint256 public totalSupply;
// private state variable
address private _owner;
mapping(address => uint256) private _balanceOf;

之后,如果有事件需要声明,则声明事件。再之后,如果有错误需要声明,则声明错误。

事件和错误,通常会在 interface 中进行声明,所以直接在 contract 里声明的情况相对比较少。

然后,就是声明自定义的 modifiers 了。另外,modifier 的命名采用小驼峰式命名法,以下是一个示例:

modifier onlyOwner() {if(msg.sender != _owner) revert OwnableUnauthorizedAccount(msg.sender);_;
}

最后,就是一堆函数定义了。因为函数众多,所以我们还需要再进行编排,可按照如下顺序编排函数:

  • 构造函数
  • receive 函数
  • fallback 函数
  • external 函数
    • external payable
    • external
    • external view
    • external pure
  • public 函数
    • public payable
    • public
    • public view
    • public pure
  • internal 函数
    • internal
    • internal view
    • internal pure
  • private 函数
    • private
    • private view
    • private pure

可见,先定义构造函数,接着定义 receivefallback 回调函数,如果没有则不需要。

之后,根据可见性进行排序,顺序为 external、public、internal、private

不同的可见性函数内,又再根据可变性再进行排序,以 external 类函数为例,顺序为 payable 函数、external 写函数、external view 函数、external pure 函数。其他可见性类型函数也是同理。

另外,关于 internalprivate 函数的命名,建议和私有状态变量一样,添加前下划线作为前缀,以和外部函数区分。如下示例:

// internal functions
function _transfer(address from, address to, uint256 value) internal {}
function _getBalance(address account) internal view returns (uint256) {}
function _tryAdd(uint256 a, uint256 b) internal pure returns (uint256) {}// private functions
function _turn() private {}
function _isParsed() private view returns (bool) {}
function _canAdd(uint256 a, uint256 b) private pure returns (uint256) {}

最后,每个函数里的多个函数修饰符也需要进行排序,一般建议按照如下顺序进行排序:

  • 可见性(external/public/internal/private)
  • 可变性(payable/view/pure)
  • Virtual
  • Override
  • 自定义 modifiers

直接看以下例子:

function mint() external payable virtual override onlyOwner {}

以上就是关于 contract 部分的编码规范建议了。

注释

最后聊聊注释。

首先,要记住一点:良好的命名即注释

即合约、事件、错误、状态变量、函数、参数等等,所有的命名本身都应该能很好地说明其所代表的含义,这本身就是一种最直观的注释。额外添加的注释更多其实是为了进行补充说明用的。

Solidity 里支持的注释标签包括以下这些:

  • @title
  • @author
  • @notice
  • @dev
  • @param
  • @return
  • @inheritdoc
  • @custom:...

这些标签的说明在官方文档中也有说明,如下图:

在这里插入图片描述

官方编码规范建议对于所有公开的接口添加完全的注释,也即是说,我们可以给 interface 添加完整注释。Uniswap 就是一个很好的实践方,你可以看到 Uniswap 的所有 interface 都有很完整的注释。

另外,添加的注释内容还可以在区块浏览器上看到注释内容。比如,我们查看 COMP 代币合约:

  • https://etherscan.io/address/0xc00e94cb662c3520282e6f5717214004a7f26888#code

在交互页面,点开 approve 时如下:

在这里插入图片描述

再查看源代码里该 approve 函数的注释:

/*** @notice Approve `spender` to transfer up to `amount` from `src`* @dev This will overwrite the approval amount for `spender`*  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)* @param spender The address of the account which may transfer tokens* @param rawAmount The number of tokens that are approved (2^256-1 means infinite)* @return Whether or not the approval succeeded*/
function approve(address spender, uint rawAmount) external returns (bool) {...
}

可以看出,@notice@dev@param 的内容都展示在了区块浏览器交互页面上了。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【ASE】第一课_双面着色器

今天我们一起来学习ASE插件&#xff0c;希望各位点个关注&#xff0c;一起跟随我的步伐 今天我们来学习双面着色器&#xff0c;对颜色和贴图进行差值&#xff0c;双面显示不同的效果 最终效果&#xff1a; 思路&#xff1a; 1.先确定前后面的贴图和颜色 贴图&#xff08;Alb…

高效工程师的七个习惯

原文 我曾与一些杰出的工程师共事过 – 在诸如 FAANG 的大公司&#xff0c;也在初创规模的小公司。他们让我看到&#xff0c;传说中的「10 倍」工程师&#xff0c;真实存在&#xff01; 如今&#xff0c;这些工程师中&#xff0c;有些人后来创办了自己的公司&#xff0c;他们…

kmp快速匹配

用处&#xff1a;对于一个较长的字符串A&#xff0c;判断A中是否存在字符串B。 思路&#xff1a; 暴力的做法是从A的每个元素开始&#xff0c;依次比较看是否有和B相同的子串&#xff0c;时间复杂度是o&#xff08;N*N&#xff09; 优化思路是对于每次查找完成以后&#xff…

springboot+vue宠物医院挂号看病诊断系统 f9h46

目录 宠物主人宠物医生系统管理人员系统实现截图技术介绍核心代码部分展示详细视频演示源码获取 宠物主人 登录注册&#xff1a;注册账户并登录系统。 首页&#xff1a;显示系统基本信息和用户导向功能。 个人中心&#xff1a;更新个人信息&#xff0c;包括联系方式、密码等。…

【AI创作组】工程方向的硕士研究生学习Matlab的路径

1. MATLAB软件概述 1.1 MATLAB发展历程 MATLAB自20世纪70年代诞生以来,已经经历了多次重要的版本更新和功能扩展。 初始版本:MATLAB的前身只是一个简单的交互式矩阵计算器,由Cleve B. Moler博士在1970年代初期开发,目的是为了方便学生和研究人员使用线性代数软件包LINPAC…

农业与植物基因组分析专家—优青博导提供从实验设计、数据分析到SCI论文咨询的一站式服务。多年经验,精准高效,为农业科研保驾护航!

&#x1f31f; 教授团队领衔&#xff0c;全方位服务&#xff01; &#x1f680; 从实验设计到论文发表&#xff0c;一站式解决方案&#xff01; &#x1f4c8; 选择我们&#xff0c;加速您的科研进程&#xff0c;让成果不再等待&#xff01; &#x1f4dd; 专业分析 定制服…

ubuntu安装gitlab-runner

目录 1.添加gitlab 仓库地址 ​编辑2. 安装gitlab-runner命令 1.添加gitlab 仓库地址 curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash2. 安装gitlab-runner命令 sudo apt-get install -y gitlab-ru…

Python3爬虫教程-HTTP基本原理

HTTP基本原理 1&#xff0c;URL组成部分详解2&#xff0c;HTTP和HTTPS3&#xff0c;HTTP请求过程4&#xff0c;请求&#xff08;Request&#xff09;请求方法&#xff08;Request Method&#xff09;请求的网址&#xff08;Request URL&#xff09;请求头&#xff08;Request H…

招联金融内推(深圳武汉大量招后端、算法)---2025秋招内推

【投递方式】 直接扫下方二维码&#xff0c;或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus&#xff0c;使用内推码 igcefb 投递&#xff09; 【招聘岗位】 后台开发 前端开发 数据开发 数据运营 算法开发 技术运维 软件测试 产品策…

HubSpot一站式增长平台,让获客变得超简单

在这片浩瀚的商业海洋中&#xff0c;每一位企业家都是一位勇敢的航海家&#xff0c;驾驶着自己的船只&#xff0c;追逐着心中的梦想与远方。然而&#xff0c;风浪无情&#xff0c;竞争激烈&#xff0c;如何才能确保你的航程既平稳又快速&#xff1f;HubSpot&#xff0c;正是那位…

宠物去浮毛救星?希喂、小米、霍尼韦尔宠物空气净化器哪款好用

怎么有人放假也不开心&#xff1f; 快到的国庆假期真是愁死我了...本来我妈国庆去旅游&#xff0c;我就打算不回家&#xff0c;和我家猫过二人世界。结果突然有事&#xff0c;我妈取消出行&#xff0c;改成让我假期回家陪她。我回家容易&#xff0c;我家猫回去可难啊&#xff…

【C++】拆分详解 - string类

文章目录 一、为什么学习string类&#xff1f;二、标准库中的string类  1. 定义  2. 常用接口说明     2.1 构造     2.2 容量操作     2.3 访问及遍历操作     2.4 修改操作     2.5 非成员函数 三、OJ练习自测  [1. 仅仅反转字母](https://leetcod…

基于DeepFace深度学习模型的离线版人脸识别API接口实现(类似百度在线API接口)

一 背景 人脸识别技术经过数年的发展&#xff0c;在技术算法、识别性能、应用场景以及隐私保护和数据安全等方面都取得了显著的进步和成熟。 应用场景 门禁系统&#xff1a; 在门禁系统中&#xff0c;离线人脸识别可用于身份验证&#xff0c;用户只需站在摄像头前&#xff0…

明明没违规,应用还是被判恶意软件?可能是这些原因

作为Google Play上架应用的开发者&#xff0c;大家的普遍感受&#xff1a;比起写代码&#xff0c;上架的过程简直更让人心力交瘁&#xff01;特别是涉及用户数据和隐私保护的时候&#xff0c;稍有疏忽&#xff0c;就可能面临应用被下架、甚至账号被封的风险。 最近听到很多开发…

FPGA随记——VIVADO中ASYNC_REG指令

参考文章&#xff1a;Vivado综合属性系列一、ASYNC_REG_asyncregtrue-CSDN博客 -很棒棒的 跨时钟域设计&#xff08;CDC&#xff09;是个老生常谈的问题&#xff0c;其场景很多很杂&#xff0c;其中一个比较为人熟知的就是单bit信号从慢时钟到快时钟所采用的两级寄存器处理的…

一种求解城市场景下无人机三维路径规划的高维多目标优化算法,MATLAB代码

在城市环境下进行无人机三维路径规划时&#xff0c;需要考虑的因素包括高楼、障碍物、飞行安全和效率等。为了解决这些问题&#xff0c;研究者们提出了多种算法&#xff0c;包括基于智能优化算法的方法。 首先&#xff0c;无人机航迹规划问题的数学模型需要考虑无人机的基本约…

Spring Boot集成Redis Search快速入门Demo

1.什么是Redis Search&#xff1f; RedisSearch 是一个基于 Redis 的搜索引擎模块&#xff0c;它提供了全文搜索、索引和聚合功能。通过 RedisSearch&#xff0c;可以为 Redis 中的数据创建索引&#xff0c;执行复杂的搜索查询&#xff0c;并实现高级功能&#xff0c;如自动完…

【第十二周】李宏毅机器学习笔记10:生成式对抗网络2

目录 摘要Abstract1.GAN is Still Challenging2.Evaluation of Generation2.1 Mode Collapse2.2.Mode Dropping2.3.Diversity 3.Conditional GAN4.Learning from Unpaired Data总结 摘要 本周主要学习了上周关于生成式对抗网络的剩余知识&#xff0c;了解了为什么 GAN 难以训练…

【数字】flexnoc Qos配置

对于Qos_generator 的regulator模式来说可以配置的寄存器如下表&#xff1a; 因为我们没有使用external的clk来做count&#xff0c;也没有使用外部的threshold&#xff0c;所以都是使用的internal的时钟。 根据文档讲解的概念Bandwidth设置为下&#xff1a; Urgency使用来源如…

Nat Med.作者提供全文的绘图代码,对于学习作图很有帮助(一)

本期教程 获得本期教程全文代码&#xff1a;在订阅号后台回复关键词&#xff1a;20240923 2022年教程总汇 2023年教程总汇 引言 今天分享的文章是2024发表在Nat Med.期刊中&#xff0c;来自上海交通大学医学院的文章&#xff0c;作者提供了全文的绘图代码&#xff0c;确实勇&a…