Solidity 开发环境
Solidity 开发环境
- Solidity编辑器:Solidity编辑器是⼀种专⻔⽤于编写和编辑Solidity代码的编辑器。常⽤的Solidity编辑器包括
Visual Studio Code、Atom和Sublime Text。 - 以太坊开发环境:以太坊开发环境(Ethereum Development Environment)是⼀种专⻔⽤于以太坊开发的⼯
具,可以⽤于部署、测试和调试智能合约。常⽤的以太坊开发环境包括Truffle、Embark和Buidler
(Hardhat)。 - 以太坊虚拟机:以太坊虚拟机(Ethereum Virtual Machine,EVM)是以太坊区块链的运⾏环境,⽤于在以太
坊区块链上运⾏智能合约。常⽤的以太坊虚拟机包括ganache-cli和geth。 - 以太坊浏览器:以太坊浏览器是⼀种⽤于浏览、搜索和交互以太坊区块链数据的⼯具。常⽤的以太坊浏览器
包括Etherscan、Etherchain和Blockchain Explorer。
Hardhat
https://hardhat.org/
Hardhat是⽬前最好的框架之⼀,⽀持快速测试,同时提供了最好的教程和最简单的集成。 ⽼实说,每个喜欢JS框
架的⼈都应该在某个时候试⽤Hardhat。它真的很容易上⼿,具有快速的测试, ⽽且⼊⻔⾮常简单。Hardhat的
Discord也总是⾮常迅速地回答问题,因此,如果遇到问题,你 总是可以寻求帮助。Hathat使⽤Waffle和Ethers.js进
⾏测试 —— 可以说是更好的JavaScript 智能合约框架 —— 开发⼈员的⽣活质量确实能得到⼀些改善。
Hardhat还可以与OpenZeppelin的可升级智能合约插件直接集成,这是⼀个巨⼤的胜利。 这个项⽬给⼈⼀种很棒的
感觉:很⼲净。它会执⾏你想要的操作。真的很快。该项⽬正在不断改进, Hardhat显然致⼒于使智能合约开发⼈
员的⽣活更轻松。
Hardhat概要:
ETH基⾦会资助的项⽬,以前的名字是Builder
技术:Javascript,Web3.js和Ethers.js插件,OpenZeppelin可升级合同插件, Etherscan插件,区块链分叉
区块链:Hardhat运⾏时环境/本地、测试⽹、主⽹
测试⽀持:Waffle
维护:⾮常活跃
⽀持:活跃
开源
Truffle
https://trufflesuite.com/
⼏年来Truffle⼀直是以太坊智能合约的默认开发框架,这是有充分理由的。 Truffle是⼀个强⼤的框架,为其他许多
⼈树⽴了标准。你很容易找到使⽤此平台的项⽬, 因此查找示例很容易。Truffle也可以很容易地与它的姊妹⼯具
Drizzle和Ganache集成在⼀起。 特别是Ganache,它是⼯程师运⾏本地区块链的最流⾏⽅法之⼀。对于那些正在寻
找更多⼯具的⼈, 你可以为升级的Truffle团队帐户付费,并可以访问智能合约的持续集成,可视化部署和监视。
Truffle还可以与OpenZeppelin的可升级智能合约插件直接集成,这是⼀个巨⼤的胜利。 Truffle的开发团队显然是⼀
群有才华的⼯程师,他们想要使世界成为⼀个更好的智能合约场所。
Truffle测试的运⾏速度不如hardhat那样快,并且由于⽤户数量众多,获得⽀持可能很困难。 我很期待看到被
ConsenSys收购后他们将如何改善这个项⽬。Truffle的⽂档质量似乎开始下降 并且很难遵循,但是如果你⽤Google
搜索遇到的错误,则很可能会遇到遇到该错误并已解决的⼈。 我发现改善项⽬的⼀些最佳⽅法是在GitHub上发布
问题。⽆论如何,保持⽣态系统不断壮⼤是我们 的开源职责!
由于⼏乎每个⼈都熟悉它,因此获得同⾏的⽀持通常很容易。我真的希望看到团队在这个项⽬ 上获得更多⽀持,
因为他们有这么多⽤户。我希望他们能看到本⽂并致⼒于改善其⽂档,以使 其能够继续作为测试和部署智能合约
的⾸选平台之⼀。
Truffle概要:
使⽤最⼴泛的平台;最近被ConsenSys收购(2020年11⽉)
技术:Javascript,Web3.js,OpenZeppelin可升级合同插件,Etherscan插件,区块链分叉
区块链:Ganache /本地,测试⽹,主⽹
有测试
维护:⾮常活跃
⽀持:活跃
开源,可以付费升级
Embark
Embark是整个DAPP框架。这是⼀个全栈的区块链平台。在Gitter的 ⼀些帮助下,我能够将Chainlink合约部署到
Kovan⽹络。它带有⼀个UI,允许你在GUI中与区块链 和合约进⾏交互。Embark有⼀段学习曲线,我没有花⾜够的
时间来克服,但它展示了其潜⼒。这就是 为什么我想将其包括在这⾥的原因,因为我觉得我没有完全消化很多东
⻄。
我希望看到⼈们更多地尝试该框架并看到其功能。由于在项⽬中花费的时间有限,我觉得我可能 ⽆法在这⾥做到
公正。我确实认为将前端与后端解耦仍然是最佳做法,但是如果你需要启动⼀个 具有良好前端的项⽬并且不关⼼
解耦,那么您应该100%尝试这个项⽬。
这是⼀个很酷的项⽬,如果有⼈喜欢Hardhat和Truffle,并且⼜想与全栈解决⽅案集成,那么我愿意 推荐Embark给
他。
Embark概要:
具有⼤量功能的JavaScript框架⽤于前端开发
技术:JavaScript,Web3.js,代理合约⽀持
区块链:Ganache /本地,测试⽹,主⽹
⽀持测试
维护:轻度活跃
⽀持:活跃
开源
Remix基本使⽤
Remix 是以太坊智能合约编程语⾔Solidity IDE,其实基于浏览器的IDE,有⼀个很⼤的好处就是不⽤安装,打开即
⽤。
官⽹ https://remix.ethereum.org/。
Remix基本功能
合约创建 - 如上图创建⼀个空的⼯作空间
- 在⼯作空间下创建⼀个智能合约⽂件,ex: HelloWord.sol
智能合约⽂件以 .sol 结尾,
⽂件名采⽤⼤驼峰命名法
⽂件名和合约名保持⼀致 - 编写合约代码
合约编译
/" SPDX-License-Identifier: MIT;
/" 智能合约的许可协议
pragma solidity ^0.8.7;
/" 智能合约的适⽤版本
contract HelloWord {
string name;
function get() public view returns (string memory){
return name;
}
function set (string memory _name) public {
name = _name;
}
}
编译结果:
⽬录产⽣⼀个 artifacts ⽂件夹
合约部署
- 通过第四个菜单进⼊部署界⾯
- 选择部署环境
- 选择部署合约的账户地址
- 设置gas限制
- 选择要部署的合约
- deploy 按钮进⾏部署
部署成功效果
合约调试 - 通过函数的返回值查看变量
- event Log
solidity默认没有consol.log 或者 print 类似的事件系统 但是我们可以通过,注册事件查看对应的log⽇志
/" SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Variables {
event Log(address);
event Log(uint);
function doSomething() public {
uint timestamp = block.timestamp; /" Current block timestamp
address sender = msg.sender; /" address of the caller
emit Log(timestamp);
emit Log(sender);
}
}
本地部署ReMix IDE
在线Remix访问缓慢,如果你有很好的⽹络环境也可以本地部署⼀套。
在本地部署Remix需要准备两个东⻄:⼀个是Remix-project,可以理解为Remix的前端;另⼀个是Remixd,可以理
解为Remix的后端,它们的Github仓库地址分别是:
https://github.com/ethereum/remix-project
https://github.com/ethereum/remix-project/tree/master/libs/remixd
Docker⽅式安装
如果要运⾏合并到主分⽀中的最新更改,请运⾏:
如果你想运⾏最新的 remix-live 版本运⾏。
docker-compose⽅式安装
docker pull remixproject/remix-ide:latest
docker run -p 8080:80 remixproject/remix-ide:latest
docker pull remixproject/remix-ide:remix_live
docker run -p 8080:80 remixproject/remix-ide:remix_live
部署 Remixd
Remixd 的安装使⽤步骤如下:
1.通过 npm 或者 yarn 安装 Remixd(建议⽤yarn)
npm 命令:
2.启动 Remix-IDE
3.在 Remix-IDE 上点两下
⼀个点 Solidity,选择相应环境;另⼀个点 Connect to Localhost,连接本地环境
docker-compose pull
docker-compose up -d
npm install -g @remix-project/remixd
yarn add global @remix-project/remixd
remixd -s ./shared_project -u http:/“localhost:8080 1
Solidity基础介绍
认识⼀个最简单的存储合约
/” SPDX-License-Identifier: MIT;
/" 智能合约的许可协议
pragma solidity ^0.8.7;
/" 智能合约的适⽤版本
import “”;
/" 导⼊
contract HelloWord {
string name;
/" 状态变量
/" 函数
function get() public view returns (string memory){
return name;
}
function set (string memory _name) public {
name = _name;
授权协议
默认情况下,在发布源代码时加⼊机器可读许可证说明是很重要的。由于提供源代码总是涉及版权⽅⾯的法
律问题,Solidity 编译器⿎励使⽤机器可读的 SPDX 许可证标识符,⽐如: /" SPDX-License-Identifier:
MIT
⾄于什么是SPDX ⼤家可以参考如下资料:
⾸先是SPDX,这是个组织名,其⽹站为:
SPDX 许可证列表 |软件包数据交换 (SPDX)
SPDX-License-Identifier 组合起来就是在指SPDX的许可证列表
后⾯的格式为 :+ SPDX的许可证列表中的某个许可证
⽐如上⾯例⼦中的MIT 许可:
MIT 基本信息
全名:MIT License 麻省理⼯学院许可证 标志符: MIT
MIT的具体内容
特此免费授予获得(“软件”)副本的任何⼈不受限制地处理本软件的许可,包括但不限于使⽤、复制、修改、合
并、发布、分发、再许可和/或出售本软件副本的权利
Solidity版本限制
第⼆⾏是告诉编译器源代码所适⽤的Solidity版本为>=0.7.0 及 <0.9.0 。这样的说明是为了确保合约不会在新的
编译器版本中发⽣异常的⾏为。关键字 pragma 是告知编译器如何处理源代码的通⽤指令
执⾏原理
在⼀个去中⼼化的世界,我们的程序并不紧紧活⼀台机器的CPU 上, 在⼀个去中⼼化的世界是由很多节点组成的
P2P ⽹络。合约代码会在各节点上[ Full Node ]单独运⾏,⽽事实上P2P的各节点相互之间都不信任的,所以每个节
点都会存⼀份⾃⼰的状态(Distributed Ledger,分布式账本),在该示例就是name,当调⽤set()的时候,⼤家都改
变了name,此时需要⼀种共识机制(PoS),如果PoS认为name合法,此次调⽤完成。 否则回滚上⼀个name的
值,因此每⼀次改变状态变量的调⽤都是以⼀个事务Transcation来执⾏。
Api⽂档
https://solidity-by-example.org/
变量
}
}
名称 返回
blockhash(uint blockNumber) returns
(bytes32)
给定区块的哈希值 – 只适⽤于256最近区块, 不包含当前区
块。
block.coinbase (address payable) 当前区块矿⼯的地址
block.difficulty (uint) 当前区块的难度
block.gaslimit (uint) 当前区块的gaslimit
block.number (uint) 当前区块的number
block.timestamp (uint) 当前区块的时间戳,为unix纪元以来的秒
gasleft() returns (uint256) 剩余 gas
msg.data (bytes calldata) 完成 calldata
msg.sender (address payable) 消息发送者 (当前 caller)
msg.sig (bytes4) calldata的前四个字节 (function identifier)
msg.value (uint) 当前消息的wei值
now (uint) 当前块的时间戳
tx.gasprice (uint) 交易的gas价格
tx.origin (address payable) 交易的发送⽅
局部变量
在函数内部声明
不存储到链上
状态变量
在函数外部声明
状态变量是永久地存储在链上的值。
全局变量
内置提供有关区块链的信息⽐如 block 、 msg 等
全局变量
这些是全局⼯作区中存在的特殊变量,提供有关区块链和交易属性的信息。
/" SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Variables {
状态变量
function doSomething() public view returns(uint, address) {
/" 内置全局变量
uint timestamp = block.timestamp; /" 获取区块时间戳
address sender = msg.sender; /" 获取区块地址
return (timestamp,sender);
}
}
局部变量
在为变量命名时,请记住以下规则。
/" SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Variables {
uint public nun = 123;
function doSomething() public{
nun +&;
}
}
/" SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Variables {
function doSomething() public pure returns(uint){
uint num = 123;
num +&;
/" 局部变量不保存每次使⽤都重置
return num;
}
}
不应使⽤ Solidity 保留关键字作为变量名。例如, break 或 boolean 变量名⽆效。
不应以数字(0-9)开头,必须以字⺟或下划线开头。例如, 123test 是⼀个⽆效的变量名,但是 _123test 是
⼀个有效的变量名。
变量名区分⼤⼩写。例如, Name 和 name 是两个不同的变量。
Solidity 可⻅性修饰符
1.public – 所有合约与账号都可以调⽤
2.private -只有在定义该函数的合约可以调⽤
3.internal- 当前合约或者继承该合约的,类似java ⾥⾯的protected 关键字。
4.external – 只有其他合约或者账号可以调⽤,定义该函数的合约不能调⽤,除⾮使⽤ this 关键字
函数
- 函数是代码的可执⾏单元。函数通常在合约内部定义,但也可以在合约外定义。
- Solidity有两个关键字与函数输出相关:return和returns,他们的区别在于:
returns加在函数名后⾯,⽤于声明返回的变量类型及变量名。
return⽤于函数主体中,返回指定的变量。 - view和pure的⽤法
getter 类型的函数可以被view 或者 pure 修饰。 view 修饰的函数不能改变状态变量。pure 则既不能改
变状态变量,也不取读取状态变量。
在我们⽆法确定该⽤view还是pure时,remix会给我们完善的提示信息
错误Errors
/" SPDX-License-Identifier: GPL-3.0
pragma solidity >(0.8.0 <0.9.0;
contract Storage {
function set() public { /" 定义函数
/" …*
}
}
function OutsideFunc(uint x) pure returns (uint) {
return x * 2;
}
/" SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ViewPureTest {
uint public x = 1;
/" 不能改变状态变量.
function addToX(uint y) public view returns (uint) {
return x + y;
}
/"函数中没有任何状态变量出现。
function add(uint i, uint j) public pure returns (uint) {
return i + j;
}
}
assert(bool condition) 1
− 如果不满⾜条件,此⽅法调⽤将导致⼀个⽆效的操作码,对状态所做的任何更改将被还原。这个⽅法是⽤
来处理内部错误的。
require(bool condition) − 如果不满⾜条件,此⽅法调⽤将恢复到原始状态。此⽅法⽤于检查输
⼊或外部组件的错误。
require(bool condition, string memory message) − 如果不满⾜条件,此⽅法调⽤将恢复到原
始状态。此⽅法⽤于检查输⼊或外部组件的错误。它提供了⼀个提供⾃定义消息的选项。
revert() − 此⽅法将中⽌执⾏并将所做的更改还原为执⾏前状态。
revert(string memory reason) − 此⽅法将中⽌执⾏并将所做的更改还原为执⾏前状态。它提供了
⼀个提供⾃定义消息的选项。
回退状态:但是gas费⽤是需要消耗
assert 合约内部错误
require 外部参数错误
assert 内部错误
Solidity 为应对失败,允许⽤户定义 error 来描述错误的名称和数据。 跟⽤错误字符串相⽐, error 更便宜并
且允许你编码额外的数据,还可以⽤ NatSpec 为⽤户去描述错误。
事件Event
事件是能⽅便地调⽤以太坊虚拟机⽇志功能的接⼝。
参数被2整除 1
/" SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
error NotFoundUser(address account, uint256 uid);
contract Token {
mapping(address =, uint256) users;
function getUser(address _account, uint256 _uid) public view {
uint256 uid = users[msg.sender];
if (uid < _uid) {
revert NotFoundUser(_account, _uid);
}
/" …*
solidity默认没有consol.log 或者 print 类似的事件系统 但是我们可以通过,注册事件查看对应的log⽇志
变量的数据位置
数据位置
在合约中声明和使⽤的变量都有⼀个数据位置,指明变量值应该存储在哪⾥。合约变量的数据位置将会影响Gas消
耗量。
/" SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Variables {
event Log(address);
event Log(uint);
function doSomething() public {
uint timestamp = block.timestamp; /" Current block timestamp
address sender = msg.sender; /" address of the caller
emit Log(timestamp);
emit Log(sender);
}
}
Solidity 提供4种类型的数据位置。
Storage
Memory
Calldata
Stack
Storage
该存储位置存储永久数据,这意味着该数据可以被合约中的所有函数访问。可以把它视为计算机的硬盘数据,所有
数据都永久存储。
保存在存储区(Storage)中的变量,以智能合约的状态存储,并且在函数调⽤之间保持持久性。与其他数据位置相
⽐,存储区数据位置的成本较⾼。
存储中的数据是永久存在的。存储是⼀个key/value库
存储中的数据写⼊区块链,因此会修改状态,这也是存储使⽤成本⾼的原因。
占⽤⼀个256位的槽需要消耗20000 gas
修改⼀个已经使⽤的存储槽的值,需要消耗5000 gas
当清零⼀个存储槽时,会返还⼀定数量的gas
存储按256位的槽位分配,即使没有完全使⽤⼀个槽位,也需要⽀付其开销
Memory
内存位置是临时数据,⽐存储位置便宜。它只能在函数中访问。
通常,内存数据⽤于保存临时变量,以便在函数执⾏期间进⾏计算。⼀旦函数执⾏完毕,它的内容就会被丢弃。你
可以把它想象成每个单独函数的内存(RAM)。
内存是⼀个字节数组,槽⼤⼩位256位(32字节)
数据仅在函数执⾏期间存在,执⾏完毕后就被销毁
读或写⼀个内存槽都会消耗3gas
为了避免矿⼯的⼯作量过⼤,22个操作之后的单操作成本会上涨
/" SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract DataLocations {
struct MyStruct {
uint256 foo;
}
mapping(uint256 =, MyStruct) public myStructs;
function funcStorage(uint8 _idx, uint _val) public {
/" 从映射中获取结构体,storage 变量改变会影响状态变量的值
MyStruct storage myStruct = myStructs[_idx];
myStruct.foo = _val;
}
}
Calldata
Calldata是不可修改的⾮持久性数据位置,所有传递给函数的值,都存储在这⾥。此外,Calldata是外部函数的参数
(⽽不是返回参数)的默认位置。
Stack
堆栈是由EVM (Ethereum虚拟机)维护的⾮持久性数据。EVM使⽤堆栈数据位置在执⾏期间加载变量。堆栈位置最多
有1024个级别的限制。
可以看到,要永久性存储,可以保存在存储区(Storage)。
数据位置规则
规则1 – 状态变量
状态变量总是存储在存储区中。
此外,不能显式地标记状态变量的位置。
/" SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract DataLocations {
struct MyStruct {
uint256 foo;
}
mapping(uint256 =, MyStruct) public myStructs;
function funcStorage(uint8 _idx, uint _val) public view returns (MyStruct memory) {
/" 从映射中获取结构体,storage 变量改变会影响状态变量的值
MyStruct memory myStruct = myStructs[_idx];
myStruct.foo = _val;
return myStruct;
}
}
pragma solidity ^0.5.0;
contract DataLocation {
/" storage
uint stateVariable;
uint[] stateArray;
}
规则2 – 函数参数与返回值
函数参数包括返回参数都存储在内存中。
此处,函数参数 uint num1 与 uint num2 ,返回值 uint result 都存储在内存中。
规则3 – 局部变量
值类型的局部变量存储在内存中。但是,对于引⽤类型,需要显式地指定数据位置。
pragma solidity ^0.5.0;
contract DataLocation {
uint storage stateVariable; /" 错误
uint[] memory stateArray; /" 错误
}
pragma solidity ^0.5.0;
contract DataLocation {
/" storage
uint stateVariable;
uint[] stateArray;
function calculate(uint num1, uint num2) public pure returns (uint result) {
return num1 + num2
}
}
pragma solidity ^0.5.0;
contract Locations {
/- 此处都是状态变量 */
/" 存储在storage中
bool flag;
uint number;
address account;
function doSomething() public {
/- 此处都是局部变量 */
/" 值类型
/" 所以它们被存储在内存中
bool flag2;
uint number2;
address account2;
不能显式覆盖具有值类型的局部变量。
规则4 – 外部函数的参数
外部函数的参数(不包括返回参数)存储在Calldata中。
赋值的数据位置规则
Solidity数据类型
Solidity 是⼀种静态类型语⾔,这意味着每个变量(状态变量和局部变量)都需要在编译时指定变量的类型。
Solidity中,变量类型有以下⼏⼤类:
值类型
地址类型
引⽤类型
/" 引⽤类型,需要显示指定数据位置,此处指定为内存
uint[] memory localArray;
}
}
function doSomething() public {
/- 此处都是局部变量 */
/" 值类型
bool memory flag2; /" 错误
uint Storage number2; /" 错误
address account2;
}
值类型
类型 保留字 取值
布尔
型 bool true/false
整型 int/uint 有符号整数/⽆符号整数。
整型 int8 to
int256
8位到256位的带符号整型数。int256与int相同。
整型 uint8 to
uint256
8位到256位的⽆符号整型。uint256和uint是⼀样的。
定⻓
浮点
型
fixed/unfixed 有符号和⽆符号的定⻓浮点型
定⻓
浮点
型
fixedMxN 带符号的定⻓浮点型,其中M表示按类型取的位数,N表示⼩数点。M应该能被8整
除,从8到256。N可以是0到80。fixed与fixed128x18相同。
定⻓
浮点
型
ufixedMxN ⽆符号的定⻓浮点型,其中M表示按类型取的位数,N表示⼩数点。M应该能被8整
除,从8到256。N可以是0到80。fixed与fixed128x18相同。
地址类型
地址类型表示以太坊地址,⻓度为20字节。地址可以使⽤ .balance ⽅法获得余额,也可以使⽤ .transfer ⽅法
将余额转到另⼀个地址。
复制
引⽤类型/复合数据类型
Solidity中,有⼀些数据类型由值类型组合⽽成,相⽐于简单的值类型,这些类型通常通过名称引⽤,被称为引⽤
类型。
引⽤类型包括:
数组 (字符串与bytes是特殊的数组,所以也是引⽤类型)
struct (结构体)
address x = 0x212;
address myAddress = this;
if (x.balance < 10 &2 myAddress.balance >( 10)
x.transfer(10);
map (映射)
“ undefined ”或“ null ”值的概念在Solidity中不存在,但是新声明的变量总是有⼀个 默认值 ,具体的默认值跟类
型相关。 要处理任何意外的值,应该使⽤错误处理来恢复整个交易,或者返回⼀个带有第⼆个 bool 值的元组表
示成功。
bool/布尔类型
布尔值的取值范围为 true 和 false 。
默认值: false
运算符:
●!(逻辑⾮)
●&& (逻辑与, “and” )
●|| (逻辑或, “or” )
●== (等于)
●!= (不等于)
int、uint/整数类型
int/uint: 变⻓的有符号或⽆符号整型。变量⽀持的步⻓以 8 递增,⽀持从 uint8 到 uint256 ,以及
int8 到 int256 。需要注意的是, uint 和 int 默认代表的是 uint256 和 int256 。
int 有符号整型(包含负数)
默认为 int256
不同位⻓的整形范围如下:
int8 取值范围:-(2 ** 7)到 2 ** 7 -1
int16 取值范围:-(2 ** 15)到 2 ** 15 -1
…
intX 取值范围:-(2 X -1)到 2( X -1) -1
pragma solidity ^0.8.0;
contract TestBool {
error NotEqual(bool A,bool B);
bool public A; /" false
bool public B = true; /“true
/” require(A=4B,“A not equal B”);
if (A !6 B) {
error NotEqual(A,B);
}
}
int256 取值范围:-(2 ** 255)到 2 ** 255 -1
uint ⽆符号整型
默认为 uint256
不同位⻓的整形范围如下:
uint8 取值范围:0 到 2 ** 8 - 1
uint16 取值范围:0 到 2 ** 16 - 1
…
uintX 取值范围:0 到 2 ** X - 1
uint256 取值范围:0 到 2 ** 256 - 1
对于整形 X,可以使⽤ type(X).min 和 type(X).max 去获取这个类型的最⼩值与最⼤值。
address/地址
默认值: 0x0000000000000000000000000000000000000000
20字节的16进制地址⽤来表示⼀个账户 或者合约地址
/" SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TestIntval {
int8 public i8 = -1;
int public i256 = 456;
int public i = -123; /" int 等同于 int256
/" int 的最⼤最⼩值
int public minInt = type(int).min;
int public maxInt = type(int).max;
uint8 public u8 = 1;
uint256 public u256 = 456;
uint public u = 123; /" uint 等同于 uint256
/" uint 的最⼤最⼩值
uint public minUInt = type(uint).min;
uint public maxUInt = type(uint).max;
function mini() public pure returns(uint8){
return type(uint8).max;
}
}
/" SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TestAddress {
/"与其他机器语⾔相区别的类型就是这个address 类型,160-bit/20byte
bytes/字节数组
在计算机中的最⼩存储单位是 bit(位)
1byte等于8位
Solidity中,byte可以赋值为
16进制数字
单引号的单个或多个字符
定⻓字节数组
bytes1 后⾯数字1是表示1字节 bytes默认等于bytes1
Bytes2 后⾯数字2是表示2字节
Bytes3 后⾯数字3是表示3字节
bytes4 后⾯数字4是表示4字节
…
bytes32 后⾯数字32是表示32字节
bytes32 等价于 int256或uint256 的位数
成员变量
.length 表示这个字节数组的⻓度(只读)
string/字符串
-
中⽂特殊字符需要⽤ unicode 编码
-
通过concat ⽅法进⾏拼接
-
bytes 和 string之间转化
address public myAddr = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
/"合约⾃⼰的地址
address contractAddress = address(this);
/“跟普通的地址类型⼀样,但多了两个⽅法 transfer/send 这两个⽅法后⾯章节会讲到
/” address sender = payable(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4);
/"可以使⽤ balance 属性来查询⼀个地址的余额
function getBalance()
public view
returns (uint256, uint256)
{
require(myAddr.balance < contractAddress.balance, “1 must lg 2”);
return (myAddr.balance, contractAddress.balance);
}
} -
string 字符串不能通过 length ⽅法获取其⻓度。
-
keccak256(abi.encodePacked(s1)) =4 keccak256(abi.encodePacked(s2)) 可以通过这个⽅法⽐较
两个字符串是否相等。 -
abi.encodePacked(s1, s2) :通过这个⽅法进⾏字符串合并拼接。
/" SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TestAddress {
string public str1 = “123”;
/" 中⽂不适⽤unicode编码报错
/" string public str2 = =“你好”;
string public str2 = unicode"abc";
function concat() public view returns(string memory) {
string memory result = string.concat(str1,str2);
return result;
}
function caoncat2(string memory _a, string memory _b) public pure returns(string
memory) {
return string.concat(_a,_b);
}
function caoncat3(string memory _a, string memory _b) public pure returns(bytes
memory) {
bytes memory _ba = bytes(_a);
bytes memory _bb = bytes(_b);
return bytes.concat(_ba,_bb);
}
function caoncat4(string memory _a, string memory _b) public pure returns(string
memory) {
bytes memory _ba = bytes(_a);
bytes memory _bb = bytes(_b);
return string(bytes.concat(_ba,_bb));
}
/" ⽐较s1和s2是否相等,相等返回true,不相等返回false
function compareEqual(string memory s1, string memory s2)
public
pure
returns (bool)
{
/" 不⽀持字符直接⽐较
return s1 =4 s2;
/" return keccak256(abi.encodePacked(s1)) =4 keccak256(abi.encodePacked(s2));
}
/" 将s1和s2合并为⼀个字节数组
function mergeS1AndS2ReturnBytes(string memory s1, string memory s2)
public
pure
returns (bytes memory)
{
Enum(枚举)
枚举将⼀个变量的取值限制为⼏个预定义值中的⼀个。精确使⽤枚举类型有助于减少代码中的bug。
array/数组
T[k]: 元素类型为T,固定⻓度为K的数组 uint[5]
return abi.encodePacked(s1, s2);
}
/" 将s1和s2合并为⼀个字节数组转换为string
function mergeS1AndS2ReturnString(string memory s1, string memory s2)
public
pure
returns (string memory)
{
return string(abi.encodePacked(s1, s2));
}
}
ontract UserState {
/" 枚举
/“默认值是列表中的第⼀个元素
enum State {
Online, /” 0
Offline, /" 1
Unknown /" 2
}
State public status;
function get() public view returns (State) {
return status;
}
/" 通过将uint传递到输⼊来更新状态
function set(State _status) public {
status = _status;
}
/" 也可以是这样确定属性的更新
function off() public {
status = State.Offline;
}
/" delete 将枚举重置为其第⼀个值 0
function reset() public {
delete status;
}
}
T[]: 元素类型为T, ⻓度可以动态调整
⼀、固定⻓度的数组(Arrays)
/" SPDX-License-Identifier: GPL-3.0
pragma solidity >(0.7.0 <0.9.0;
contract MappingTest {
/" 创建定⻓数组
uint256[5] public arr = [1, 2, 3, 4, 5];
/" 定⻓数组求和
function getAll() public view returns (uint256) {
uint256 num = 0;
for (uint256 i = 0; i < arr.length; i+&) {
num += arr[i];
}
return num;
}
function get() public view returns (uint256[5] memory) {
return arr;
}
/" 获取定⻓数组⻓度
function getLenth() public view returns (uint256) {
return arr.length;
}
/" 修改⻓度失败
function changeLenth() public {
/" arr.length = 7;
}
/" 修改内部数据
function change(uint256 _idx, uint256 _val) public {
arr[_idx] = _val;
}
/" push 修改
/" function change(uint256 _val) public {
/" arr.push(_val);
/" }
}
⼆、可变⻓度的Arrays
uint [] T = [1,2,3,4,5] ,这句代码表示声明了⼀个可变⻓度的 T 数组,因为我们给它初始化了 5 个⽆符号
整数,所以它的⻓度默认为 5 。
(0.5.0 的版本中 length ⽅法只读,不可修改。 5版本之前⽀持length属性修改,缺失的以0补位
三、⼆维数组 - 数组⾥⾯放数组
uint [] T = [1,2,3,4,5]; 1
/" SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract C {
uint256[2][3] T = [[1, 2], [3, 4], [5, 6]];
function T_len() public view returns (uint256) {
return T.length; /" uint256: 3
}
function getT() public view returns (uint256[2][3] memory) {
return T;
}
function change() public {
T[1][0] = 55;
}
}
uint [2][3] T = [[1,2],[3,4],[5,6]] 这是⼀个三⾏两列的数组,你会发现和Java、C语⾔等的其它语⾔中
⼆位数组⾥⾯的列和⾏之间的顺序刚好相反。在其它语⾔中,上⾯的内容应该是这么存储 uint [2][3] T =
[[1,2,3],[4,5,6]] 。
上⾯的 数组T 是 storage 类型的数组,对于 storage 类型的数组,数组⾥⾯可以存放任意类型的值(⽐如:其它
数组,结构体,字典/映射等等)。对于 memory 类型的数组,如果它是⼀个 public 类型的函数的参数,那么它
⾥⾯的内容不能是⼀个 mapping(映射/字典) ,并且它必须是⼀个 ABI 类型。
四、数组字⾯量 Array Literals / 内联数组 Inline Arrays
结构体 struct
通过基本数据类型来组合成⾃定义复杂的数据类型
语法结构:
声明结构体
创建结构变量
/" SPDX-License-Identifier: GPL-3.0
pragma solidity >(0.7.0 <0.9.0;
contract C {
function f() pure public {
g([1, 2, 3]);
g([uint(1),2,3])
}
function g(uint[3] memory _data) pure public {
/" …*
}
}
struct 关键字 结构体名称 {
类型1 属性名1;
类型2 属性名2;
…
}
struct Person {
uint8 age;
string id;
string name;
}
修改结构体变量
函数中返回结构体
字典/映射(Mappings)
结构体 变量名 = 结构体(属性1,属性2,…*)
结构体 变量名 = 结构体({属性1:value1,属性2:value2Ï})
Person student1 = Person(18,1,“柯南”);
Person student2 = Person({age:17, id: 2, name: “迪迦Ï”})
function setStudent(uint _age, string _name) public {
student1.age = _age
student1.name = _name
}
function getStudent() public view returns(Person) {
return student1
}
contract Structs {
struct Todo {
string text;
bool completed;
}
/" 结构体数组
Todo[] public todos;
/" 初始化结构的3种⽅法
function create(string calldata _text) public {
/" 1.像函数⼀样调⽤它
todos.push(Todo(_text, false));
/" 2. 键值对
todos.push(Todo({text: _text, completed: false}));
/" 3.初始化⼀个空结构,然后更新它
Todo memory todo;
todo.text = _text;
todos.push(todo);/" completed 没有定义,默认为 false
}
/"通过索引获取结构体数组中⼀个元素,并更新内部的属性
function update(uint _index) public {
Todo storage todo = todos[_index];
todo.completed = !todo.completed;
}
}
与数组和结构体⼀样,映射也是引⽤类型。
是⼀个⼀对⼀键值存储关系。
可以理解成js中的对象
_KeyType – 可以是任何内置类型,或者bytes和字符串。不允许使⽤引⽤类型或复杂对象。
_ValueType – 可以是任何类型。
注意
映射的数据位置(data location)只能是storage,通常⽤于状态变量。
映射可以标记为public,Solidity ⾃动为它创建getter。
mapping 不能直接在函数返回
创建mapping
mapping(_KeyType =, _ValueType) 1
contract MappingTest {
mapping(address =, uint256) public balances;
function update(uint256 _amount) public {
balances[msg.sender] = _amount;
}
function getAmount() public view returns(uint){
return balances[msg.sender];
}
}
contract LedgerBalance {
mapping(address =, uint) public balances;
function updateBalance(uint newBalance) public {
balances[msg.sender] = newBalance;
}
}
contract Updater {
function updateBalance() public returns (uint) {
LedgerBalance ledgerBalance = new LedgerBalance();
ledgerBalance.updateBalance(10);
return ledgerBalance.balances(address(this));
}
}
contract Mapping {
Solidity运算符
/"从地址到uint的映射
mapping(address =, uint) public myMap;
function get(address _addr) public view returns (uint) {
/"映射始终返回⼀个值。
/“如果从未设置该值,它将返回默认值。
return myMap[_addr];
}
/” 更新此地址的值
function set(address _addr, uint _i) public {
myMap[_addr] = _i;
}
function remove(address _addr) public {
/"将值重置为默认值
delete myMap[_addr];
}
}
/“嵌套 mapping
contract NestedMapping {
/“嵌套映射(从地址映射到另⼀个映射)
mapping(address =, mapping(uint =, bool)) public nested;
function get(address _addr1, uint _i) public view returns (bool) {
/” 可以从嵌套映射中获取值
return nested[_addr1][_i];
}
function set(
address _addr1,
uint _i,
bool _boo
) public {
nested[_addr1][_i] = _boo;
}
/” 删除 mapping 的⼀个元素
function remove(address _addr1, uint _i) public {
delete nested[_addr1][_i];
}
}
序号 运算符与描述
1 + (加) 求和 例: A + B = 30
2 – (减) 相减 例: A – B = -10
3 * (乘) 相乘 例: A * B = 200
4 / (除) 相除 例: B / A = 2
5 % (取模) 取模运算 例: B % A = 0
6 ++ (递增) 递增 例: A++ = 11
7 — (递减) 递减 例: A– = 9
序号 运算符与描述
1 == (等于)
2 != (不等于)
3 > (⼤于)
4 < (⼩于)
5 >= (⼤于等于)
6 <= (⼩于等于)
序号 运算符与描述
1 && (逻辑与) 如果两个操作数都⾮零,则条件为真。 例: (A && B) 为真
2 || (逻辑或) 如果这两个操作数中有⼀个⾮零,则条件为真。 例: (A || B) 为真
3 ! (逻辑⾮) 反转操作数的逻辑状态。如果条件为真,则逻辑⾮操作将使其为假。 例: ! (A && B) 为假
算术
⽐较
逻辑
位运算符
序
号 运算符与描述
1 & (位与) 对其整数参数的每个位执⾏位与操作。 例: (A & B) 为 2.
2 | (位或) 对其整数参数的每个位执⾏位或操作。 例: (A | B) 为 3.
3 ^ (位异或) 对其整数参数的每个位执⾏位异或操作。 例: (A ^ B) 为 1.
4 ~ (位⾮) ⼀元操作符,反转操作数中的所有位。 例: (~B) 为 -4.
5
<< (左移位)) 将第⼀个操作数中的所有位向左移动,移动的位置数由第⼆个操作数指定,新的位由0填
充。将⼀个值向左移动⼀个位置相当于乘以2,移动两个位置相当于乘以4,以此类推。 例: (A << 1) 为
4.
6 >> (右移位) 左操作数的值向右移动,移动位置数量由右操作数指定 例: (A >> 1) 为 1.
序号 运算符与描述
1 = (简单赋值) 将右侧操作数的值赋给左侧操作数 例: C = A + B 表示 A + B 赋给 C
2 += (相加赋值) 将右操作数添加到左操作数并将结果赋给左操作数。 例: C += A 等价于 C = C + A
3 −= (相减赋值) 从左操作数减去右操作数并将结果赋给左操作数。 例: C -= A 等价于 C = C – A
4 *= (相乘赋值) 将右操作数与左操作数相乘,并将结果赋给左操作数。 例: C *= A 等价于 C = C * A
5 /= (相除赋值) 将左操作数与右操作数分开,并将结果分配给左操作数。 例: C /= A 等价于 C = C / A
6 %= (取模赋值) 使⽤两个操作数取模,并将结果赋给左边的操作数。 例: C %= A 等价于 C = C % A
序号 运算符与描述
1 ? : (条件运算符 ) 如果条件为真 ? 则取值X : 否则值Y
赋值
solidity ⽀持的赋值运算符,如下表所示:
注意 – 同样的逻辑也适⽤于位运算符,因此它们将变成 <9= 、 >;= 、 >;= 、 &= 、 <= 和 ^> 。
条件运算符
Solidity循环语句
while
do…while
Solidity 中, do…while循环的语法如下:
while (表达式) {
被执⾏语句(如果表示为真)
}
/" SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract TestBool {
uint256 public num = 10;
function test(int max) public {
int256 start = 1;
while (start <? max) {
start+&;
num+&;
}
}
}
do {
被执⾏语句(如果表示为真)
} while (表达式);
注意: 不要漏掉do后⾯的分号。
/" SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract TestBool {
uint public num = 10;
function test () public returns(uint) {
int start = 1;
int max = 10;
do{
start +&;
num +&;
}while(start <?max);
return num;
}
}
for
break 与 continue
Solidity条件语句
Solidity⽀持条件语句,让程序可以根据条件执⾏不同的操作。条件语句包括:
if
for (初始化; 测试条件; 迭代语句) {
被执⾏语句(如果表示为真)
}
contract TestBool {
uint256 public num = 10;
function test(int max) public {
for(int256 start = 1; start<max; start +&) {
num +&;
}
}
}
/" SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract TestBool {
uint256 public num = 10;
function test(int max) public {
for(int256 start = 1; start<max; start +&) {
if(start =4 1) {
/" break; /" 跳出循环 num 10
continue; /" 跳出本次循环
}
num +&;
}
}
}
if…*else
if…*else if
Solidity中的函数
函数修饰符
函数修饰符⽤于修改函数的⾏为。例如,向函数添加条件限制。
修饰符定义中出现特殊符号 _ 的地⽅,⽤于插⼊函数体。如果在调⽤此函数时,满⾜了修饰符的条件,则执
⾏该函数,否则将抛出异常。
/" SPDX-License-Identifier: GPL-3.0
pragma solidity >(0.7.0 <0.9.0;
contract Owner {
address owner;
uint256 price = 10;
constructor() {
owner = msg.sender;
}
/" 定义修饰符 onlyOwner 不带参数
视图函数(view)
View(视图)函数 使⽤状态变量,但是不修改状态
如果函数中存在以下语句,则被视为修改状态,编译器将抛出警告。
修改状态变量。
触发事件。
创建合约。
使⽤ selfdestruct 。
发送以太。
调⽤任何不是视图函数或纯函数的函数
使⽤底层调⽤
使⽤包含某些操作码的内联程序集。
Getter⽅法是默认的视图函数。声明视图函数,可以在函数声明⾥,添加 view 关键字。
纯函数(Pure)
modifier onlyOwner() {
require(msg.sender =4 owner);
_;
}
/" 使⽤修饰符 onlyOwner 限制只有发布者才能调⽤
function changePrice(uint256 _price)
public
view
onlyOwner
returns (address, uint256)
{
return (owner, _price);
}
}
/" SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Test {
function getResult() public view returns (uint256, uint256) {
uint256 a = 1; /" 局部变量
uint256 b = 2;
uint256 product = a * b;
uint256 sum = a + b;
return (product, sum);
}
}
纯函数(Pure)
Pure(纯)函数不读取或修改状态。
声明纯函数,可以在函数声明⾥,添加 pure 关键字。
如果函数中存在以下语句,则被视为读取状态,编译器将抛出警告
读取状态变量。
访问 address(this).balance 或
访问任何区块、交易、msg等特殊变量(msg.sig 与 msg.data 允许读取)。
调⽤任何不是纯函数的函数。
使⽤包含特定操作码的内联程序集。
如果发⽣错误,纯函数可以使⽤ revert() 和 require() 函数来还原潜在的状态更改。
函数重载
同⼀个作⽤域内,相同函数名可以定义多个函数。这些函数的参数(参数类型或参数数量)必须不⼀样。仅仅
是返回值不⼀样不被允许。
加密函数
Solidity 提供了常⽤的加密函数。以下是⼀些重要函数:
keccak256(bytes memory) returns (bytes32) 计算输⼊的Keccak-256散列。
sha256(bytes memory) returns (bytes32) 计算输⼊的SHA-256散列。
/" SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Test {
function getSum(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
function getSum(
uint256 a,
uint256 b,
uint256 c
) public pure returns (uint256) {
return a + b + c;
}
}
ripemd160(bytes memory) returns (bytes20) 计算输⼊的RIPEMD-160散列。
ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address) 从椭圆曲
线签名中恢复与公钥相关的地址,或在出错时返回零。函数参数对应于签名的ECDSA值: r – 签名的前32
字节; s: 签名的第⼆个32字节; v: 签名的最后⼀个字节。这个⽅法返回⼀个地址。
智能合约
合约继承
就像Java、C++中,类的继承⼀样,Solidity中,合约继承是扩展合约功能的⼀种⽅式。Solidity⽀持单继承和
多继承。
Solidity中,合约继承的重要特点:
派⽣合约可以访问⽗合约的所有⾮私有成员,包括内部⽅法和状态变量。但是不允许使⽤ this 。
如果函数签名保持不变,则允许函数重写。如果输出参数不同,编译将失败。
可以使⽤ super 关键字或⽗合同名称调⽤⽗合同的函数。
在多重继承的情况下,使⽤ super 的⽗合约函数调⽤,优先选择被最多继承的合约。
contract Test {
function callKeccak256() public pure returns(bytes32 result){
return keccak256(“ABC”);
}
}
/" SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Base {
uint256 private data;
uint256 public info;
constructor() {
info = 10;
}
function increment(uint256 a) private pure returns (uint256) {
return a + 1;
}
function updateData(uint256 a) public {
data = a;
}
function getData() public view returns (uint256) {
return data;
}
构造函数
构造函数是使⽤ construct 关键字声明的特殊函数,⽤于初始化合约的状态变量。合约中构造函数是可选
的,可以省略。
构造函数有以下重要特性:
⼀个合约只能有⼀个构造函数。
构造函数在创建合约时执⾏⼀次,⽤于初始化合约状态。
在执⾏构造函数之后,合约最终代码被部署到区块链。合约最终代码包括公共函数和可通过公共函数访问的
代码。构造函数代码或仅由构造函数使⽤的任何内部⽅法不包括在最终代码中。
构造函数可以是公共的,也可以是内部的。
内部构造函数将合约标记为抽象合约。
如果没有定义构造函数,则使⽤默认构造函数。
function compute(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
}
contract Test is Base {
uint256 private result;
Base private base;
constructor() {
base = new Base();
}
function getComputedResult() public {
result = compute(3, 5);
}
function getResult() public view returns (uint256) {
return result;
}
}
相关文章:
Solidity 开发环境
Solidity 开发环境 Solidity编辑器:Solidity编辑器是⼀种专⻔⽤于编写和编辑Solidity代码的编辑器。常⽤的Solidity编辑器包括 Visual Studio Code、Atom和Sublime Text。以太坊开发环境:以太坊开发环境(Ethereum Development Environment&a…...
Ollama+Cherrystudio+beg-m3+Deepseek R1 32b部署本地私人知识库(2025年2月win11版)
之前综合网络各方面信息得到的配置表: 在信息爆炸的时代,数据安全和个性化需求愈发凸显。搭建本地私人知识库,不仅能确保数据的安全性,还能根据个人需求进行个性化定制,实现知识的高效管理和利用。随着技术的不断发展…...
vue3.0将后端返回的word文件流转换为pdf并导出+html2pdf.js将页面导出为pdf
实现思路 1.将Word文档转换为HTML:mammoth.js,它可以将.docx文件转换为HTML 2.将HTML转换为PDF:使用html2pdf.js将HTML转换为PDF 如果想要相同的效果,也可以把前端页面直接导出转换为pdf: 运用的插件:html2pdf.js 后端…...
【心得】缓存穿透与缓存击穿总是记混,一文从英语单词含义角度分析带你区分清楚!
一句话省流版:缓存穿透的英语为cache penetration,可以翻译为缓存渗透,“渗透”联想到网安的攻击渗透,故缓存穿透是绕过防护进行攻击; 缓存击穿的英语为"cache breakdown",可以翻译为缓存故障,“…...
day02
作业2 为 Activity 设置2种不同的启动模式并通过 adb 命令进行查看任务栈信息,并且打印生命周期方法执行日志 step1:生成第二个Activity和相应配置、 一个是singleTask模式,还一个是mainActivity的默认格式 step2:打印生命周期…...
JavaScript 简单类型与复杂类型-堆和栈
深入理解JavaScript中的简单类型(基本数据类型)与复杂类型(引用数据类型)如何在内存中存储对于编写高效、无误的代码至关重要。本文将探讨这两种类型的差异,以及它们在内存中的存储机制——栈(Stack&#x…...
五、AIGC大模型_04LLaMA-Factory基础知识与SFT实战
1、LLaMA-Factory 基本介绍 1.1 定义 LLaMA-Factory 是一个开源的大型语言模型(LLM)微调框架,旨在帮助开发者和研究人员轻松地对预训练语言模型进行定制化训练和优化 1.2 功能特点 支持多种预训练模型 LLaMA Factory 支持超过 100 种主流的…...
数字IC后端设计实现OCC(On-chip Clock Controller)电路介绍及时钟树综合案例
数字IC后端时钟树综合专题(OCC电路案例分享) 复杂时钟设计时钟树综合(clock tree synthesis)常见20个典型案例 1、什么是OCC? 片上时钟控制器(On-chip Clock Controllers ,OCC),也称为扫描时钟控制器(Scan Clock Con…...
LeetCodehot 力扣热题100 全排列
这段代码的目的是计算给定整数数组的所有全排列(permutations),并返回一个包含所有排列的二维数组。 思路解析 在这段代码中,采用了 深度优先搜索(DFS) 和 回溯 的方法来生成所有的排列。 关键步骤…...
深度学习c++资源库:vector容器,蓝桥杯常用算法sort,unique(排序+去重)
vector容器 1.基本概念 <vector> 是 STL 中的一个容器类,不同于普通数组的静态空间,vector可以动态扩展。 动态扩展:并不是在原空间连接新空间,而是找到更大的内存空间,将原数据拷贝到新空间,释放…...
Postgresql-重置统计信息(reset statistics)
文章目录 理解 PostgreSQL 中的 pg_stat_resetpg_stat_reset 的作用与使用时机pg_stat_reset 所需权限PostgreSQL 重置统计信息的方法重置整个database重置特定表的统计重置特定function的统计重置Statistics Collector:重置 WAL(Write-Ahead Logging)统…...
【Uniapp-Vue3】导入uni-id用户体系
在uniapp官网的uniCloud中下载uni-id用户体系 或者直接进入加载,下载地址:uni-id-pages - DCloud 插件市场 进入以后下载插件,打开HbuilderX 选中项目,点击确定 点击跳过 点击合并 右键uniCloud文件夹下的database文件夹&#x…...
【前沿探索篇七】【DeepSeek自动驾驶:端到端决策网络】
第一章 自动驾驶的"感官革命":多模态神经交响乐团 1.1 传感器矩阵的量子纠缠 我们把8路摄像头+4D毫米波雷达+128线激光雷达的融合称为"传感器交响乐",其数据融合公式可以简化为: def sensor_fusion(cam, radar, lidar):# 像素级特征提取 (ResNet-152…...
Staruml软件的介绍安装uml类图的绘制流程
文章目录 1.uml和staruml之间的关系2.软件的安装3.配置脚本语言4.Staruml创建类图4.反向工程 1.uml和staruml之间的关系 这篇文章主要是介绍这个staruml软件的安装和相关的背景介绍: 我和uml初识于IDEA软件,学习java的你一定用过这个软件,当…...
神经网络发展简史:从感知机到通用智能的进化之路
引言 神经网络作为人工智能的核心技术,其发展历程堪称一场人类对生物大脑的致敬与超越。本文将用"模型进化"的视角,梳理神经网络发展的五大关键阶段,结合具象化比喻和经典案例,为读者呈现一幅清晰的AI算法发展图谱。 一…...
快节奏生活
在当今快节奏的商务环境中,效率成为了决定企业竞争力的关键因素之一。亿可达软件连接平台,以其独特的功能和优势,为职场人士带来了前所未有的便捷与高效,成为了众多用户心中的“宝藏”工具。 1、亿可达:自动化流程的搭…...
Windows 11【1001问】通过Rufus制作Win 11系统安装U盘
随着科技的发展,Windows 11 已经成为许多用户的首选操作系统。在之前的几篇文章中,我们详细探讨了 Windows 11 的概念、安装配置要求以及如何下载 Windows 11 镜像文件,并介绍了六种不同的安装方法。本篇博客将聚焦于使用 ISO 文件安装 Windo…...
spring中手写注解开发(创建对象篇)
说明: 在spring底层中并不是我写的如此,这篇只是我用我自己的方式实现了使用注解组件扫描并且 创建对象,方法并不是很难,可以看一看,欢迎大佬评论 第一步: 我们需要自己写一个注解,我用的是idea…...
DeepSeek进入开源周,分享几点关于开源的思考
最近DeepSeek进入开源周,又把差点被大众遗忘在角落的开源话题拉了出来。 作为一个开源作者,也分享几点关于开源的思考。 AI对开源的影响 开源项目遇到的最大困难 开源项目不应该商业化 你的开源项目是垃圾...
大模型训练中的数据不平衡问题及其解决策略
目录 大模型训练中的数据不平衡问题及其解决策略 一、数据不平衡问题的影响 二、处理数据不平衡问题的方法 1. 过采样(Oversampling) 2. 欠采样(Undersampling) 3. 代价敏感学习(Cost-Sensitive Learning…...
本地部署DeepSeek R1满血版大模型
一、前言 老周上一篇分享了《本地部署DeepSeek R1大模型》,本地受硬件条件限制,只跑了80亿参数量的模型。 1.5b模型基本可以在大部分个人电脑甚至手机中运行,如果你有 6G 显存以上,那么可以部署7b模型,如果有16G显存&…...
外发抄板SCH与PCB检查系列
外发抄板SCH与PCB检查系列 一、检查到手的文件二、PCB与原理图的对应检查1.为什么要查SCH与Pcbdoc的对应?2.查询方法 三、PCB与实物的信号对应检查 一、检查到手的文件 外发出去两块板子:控制板与功率板。 抄板机构返回的文件: 1.互相独立的…...
EX_25/2/25
编写一个如下场景: 有一个英雄Hero类,私有成员,攻击,防御,速度,生命值,以及所有的set get 方法 编写一个 武器 Weapon 类,拥有私有成员攻击力,以及set get 方法 编写一个…...
C# 打印Word文档 – 4种打印方法
Word文档是日常办公和学习中不可或缺的一部分。比如在商务往来中,经常需要打印 Word 文档用于撰写和传递正式的商务信函、合作协议、项目提案等。打印出来的文档便于双方签字盖章,具有法律效力和正式性。本文将提供以下4种通过C# 打印Word文档的方法&…...
ROS的action通信——实现阶乘运算(一)
在ROS中除了常见的话题(topic)通信、服务(server)通信等方式,还有action通信这一方式,由于可以实时反馈任务完成情况,该通信方式被广泛运用于机器人导航等任务中。本文将通过三个小节的分享,实现基于action通信的阶乘运…...
Mac 上安装多版本的 JDK 且实现 自由切换
1.可以通过查看以下目录中的内容,确认当前已经安装的 jdk 版本。 cd /Library/Java/JavaVirtualMachines2.命令行查看 jdk 的安装路径 /usr/libexec/java_home -V3.下载要安装的jdk版本,修改环境变量( cat ~/.bash_profile) # 定义JDK 8和JDK 17的安装路…...
网络编程套接字
目录 一、认识端口号 二、端口号和进程ID 三、TCP/UDP协议 四、网络字节序 五、socket编程接口 5.1socket的常见API 5.2socketaddr结构 六、简单的UDP程序 七、地址转换函数 八、简单的TCP网络程序 九、Tcp协议通讯流程 9.1基于TCP协议的客户端/服务器程序的一般流…...
45.matlab产生正弦叠加信号
,...
Java23种设计模式案例
目录 一、概述 二、创建型模式 (Creational Patterns) 单例模式 (Singleton Pattern) 工厂方法模式 (Factory Method Pattern) 抽象工厂模式 (Abstract Factory Pattern) 建造者模式 (Builder Pattern) 原型模式 (Prototype Pattern) 三、结构型模式 (Structu…...
计算机毕业设计程序,定制开发服务
我们擅长的开发语言包括Python、C、Golang、Java,以及前端技术如HTML、CSS、JS和Vue。我们的服务内容丰富,能够满足您各种需求,无论是简单的功能开发还是复杂的定制项目,我们都能为您提供专业支持。若有具体需求可联系作者。 链接…...
AI提示词的种类与适合的任务
以下是提示词的主要种类及其适用任务,基于大模型特性与最佳实践总结: 一、基础提示词 零样本提示(Zero-shot Prompting) 形式:直接输入任务指令,不提供示例(如“翻译以下句子:Hello …...
【Kimi】自动生成PPT-并支持下载和在线编辑--全部免费
【Kimi】免费生成PPT并免费下载 用了好几个大模型,有些能生成PPT内容; 有些能生成PPT,但下载需要付费; 目前只有Kimi生成的PPT,能选择模板、能在线编辑、能下载,关键全部免费! 一、用kimi生成PP…...
Maven环境搭建
Maven 1. 概述 ApacheMaven是一个软件项目管理和构建工具。基于项目对象模型(POM)的概念,Maven可以从中心信息中管理项目的构建、报告和文档 理解: maven构建项目(100%)而且帮你完成jar的统一管理。 思考: 原来的jar—…...
Vue面试题
Vue面试题 Vue2.0的生命周期,每个生命周期分别做了哪些事情 create阶段:vue实例被创建。 beforeCreate: 创建前,此时data和methods中的数据都还没有初始化; created:创建完毕,data中有值,未…...
Linux时间日期类指令
1、data指令 基本语法: date : 显示当前时间date %Y : 显示当前年份date %m : 显示当前月份date %d : 显示当前哪一天date “%Y-%m-%d %H:%M:%S" : 显示年月日时分秒date -s 字符串时间 : 设置系统时…...
国标28181协议在智联视频超融合平台中的接入方法
一. 国标28181介绍 国标 28181 协议全称是《安全防范视频监控联网系统信息传输、交换、控制技术要求》,是国内视频行业最重要的国家标准,目前有三个版本: 2011 年:推出 GB/T 28181-2011 版本,为安防行业的前端设备、平…...
pyautogui库的screenshot()函数
# 方法一 screenshot pyautogui.screenshot() screenshot.save("screenshot.png")# 方法二 # 获取屏幕分辨率 screen_width, screen_height pyautogui.size()# 截取桌面屏幕 screenshot pyautogui.screenshot(region(0, 0, screen_width, screen_height)) screens…...
python与C系列语言的差异总结(3)
与其他大部分编程语言不一样,Python使用空白符(whitespace)和缩进来标识代码块。也就是说,循环体、else条件从句之类的构成,都是由空白符加上冒号(:)来确定的。大部分编程语言都是使用某种大括号来标识代码块的。下面的…...
《Linux系统编程篇》System V信号量实现生产者与消费者问题(Linux 进程间通信(IPC))——基础篇(拓展思维)
文章目录 📚 **生产者-消费者问题**🔑 **问题分析**🛠️ **详细实现:生产者-消费者****步骤 1:定义信号量和缓冲区****步骤 2:创建信号量****步骤 3:生产者进程****步骤 4:消费者进程…...
Jmeter接口并发测试
Apache JMeter 是一款开源的性能测试工具,广泛用于接口并发测试、负载测试和压力测试。以下是使用 JMeter 进行接口并发测试的详细步骤: 一、准备工作 安装 JMeter 下载地址:Apache JMeter 官网 确保已安装 Java 环境(JMeter 依…...
Python--内置函数与推导式(上)
1. 匿名函数 Lambda表达式基础 语法:lambda 参数: 表达式 特点: 没有函数名,适合简单逻辑函数体只能是单行表达式自动返回表达式结果 # 示例1:加法 add lambda a, b: a b print(add(3, 5)) # 输出 8# 示例2:字…...
Python爬虫实战:获取六图网漫画图
注意:以下内容仅供技术研究,请遵守目标网站的robots.txt规定,控制请求频率避免对目标服务器造成过大压力! 一、引言 Python 作为一种广泛应用于数据处理和网络爬虫领域的编程语言,拥有丰富的库和框架。其中,Scrapy 框架以其高效、灵活、可扩展等特点,成为构建爬虫程序的…...
《人工智能之高维数据降维算法:PCA与LDA深度剖析》
在人工智能与机器学习蓬勃发展的当下,数据处理成为关键环节。高维数据在带来丰富信息的同时,也引入了计算复杂度高、过拟合风险增大以及数据稀疏性等难题。降维算法应运而生,它能将高维数据映射到低维空间,在减少维度的同时最大程…...
第2课 树莓派镜像的烧录
树莓派的系统通常是安装在SD卡上的。SD卡作为启动设备,负责启动树莓派并加载操作系统。这种设计使得树莓派具有便携性和灵活性,用户可以通过更换SD卡来更换操作系统或恢复出厂设置。 烧录树莓派的镜像即是将树莓派镜像烧录到SD卡上,…...
网络安全复习资料
网络安全复习资料 1.计算机网络安全是指保持网络中的硬件、软件系统正常运行,使他们不因自然和人为的因素而受到破坏、更改和泄露。 2.网络安全:物理安全,软件安全,信息安全,运行安全。 3.安全防范措施:…...
PIG框架学习5——动态路由实现
一、前言 参考PIGX官方文档:https://www.yuque.com/pig4cloud/pig/ggnc3e 1.1 说明 在微服务中,定义路由一般是在网关gateway对应的配置文件中进行定义声明的,PIGX框架下,如何通过动态路由实现网关路由的实时更新? 实…...
vscode设置自动换行
vscode设置自动换行 方法 方法 点击文件->首选项->设置。搜索word wrap -> 选择 on 。 搜索Word Wrap,并把选项改为on。...
Flutter-升级Xcode后构建iOS报错
代码什么都没改动,貌似只是升级了下Xcode,构建iOS就一直报错,错误有时候还不一样。 Swift Compiler Error (Xcode): Unable to rename temporary /Users/admin/Library/Developer/Xcode/DerivedData/ModuleCache.noindex/2ZBFEEPIDQ0EY/Core…...
架构思维:架构的演进之路
文章目录 引言为什么架构思维如此重要架构师的特点软件架构的知识体系如何提升架构思维大型互联网系统架构的演进之路一、大型互联网系统的特点二、系统处理能力提升的两种途径三、大型互联网系统架构演化过程四、总结 引言 在软件开发行业中,有很多技术人可能会问…...
ElasticSearch公共方法封装
业务场景 1、RestClientBuilder初始化(同时支持单机与集群) 2、发送ES查询请求公共方法封装(支持sql、kql、代理访问、集群访问、鉴权支持) 3、判断ES索引是否存在(/_cat/indices/${indexName}) 4、判断ES…...