苏泽
大家好 这里是苏泽 一个钟爱区块链技术的后端开发者
本篇专栏++←++ 持续记录本人自学两年走过无数弯路的智能合约学习笔记和经验总结 如果喜欢拜托三连支持~
本篇主要是做一个知识的整理和规划 作为一个类似文档的作用 更为简要和明了 具体的实现案例和用法 后续会陆续给出一些demo~ 请持续关注~
目录
[本篇主要是做一个知识的整理和规划 作为一个类似文档的作用 更为简要和明了 具体的实现案例和用法 后续会陆续给出一些demo~ 请持续关注~](#本篇主要是做一个知识的整理和规划 作为一个类似文档的作用 更为简要和明了 具体的实现案例和用法 后续会陆续给出一些demo~ 请持续关注~)
["view" 关键字](#"view" 关键字)
["pure" 关键字](#"pure" 关键字)
数据类型
基本数据类型:
-
整型(uint、int):用于表示整数,可以指定位数,如
uint256
。 示例:uint256 myNumber = 10;
-
布尔型(bool):用于表示真(true)或假(false)。 示例:
bool isTrue = true;
地址类型(address):
- 用于存储以太坊地址,可以是外部账户或智能合约地址。 示例:
address myAddress = 0xAbcdef1234567890;
字符串类型(string):
- 用于存储文本字符串。 示例:
string myString = "Hello, World!";
数组类型(array):
- 用于存储相同类型的元素集合。 示例:
uint256[] myArray = [1, 2, 3];
结构体类型(struct):
用于自定义复合类型,可以包含多个字段。 示例:
Copy Code
struct Person {
string name;
uint age;
}
Person myPerson;
myPerson.name = "Alice";
myPerson.age = 25;
Enum
Enum
是枚举类型,可以通过以下语法来定义
enum Status {
Unknown,
Start,
End,
Pause
}
并通过以下语法来进行更新与初始化
// 实例化枚举类型
Status public status;
// 更新枚举值
function pause() public {
status = Status.Pause;
}
// 初始化枚举值
function reset() public {
delete status;
}
映射类型(mapping):
- 用于存储键值对数据结构。 示例:
Copy Code
mapping(address => uint256) balances;
balances[msg.sender] = 100;
关键字
"view" 关键字
用于标识一个函数不会修改合约的状态,即它只能读取数据而不能修改数据。这意味着在调用视图函数时,不会产生任何交易费用,并且不会改变合约的状态。例如:
function getName() public view returns (string memory) {return name; }
"pure" 关键字
用于标识一个函数既不会修改合约的状态,也不会读取或访问合约的存储数据。这种函数通常用于执行纯粹的计算操作,不涉及存储或外部调用。例如:
function add(uint256 a, uint256 b) public pure returns (uint256) {return ab; }
"public":
- 将函数或变量声明为公共的,可以被合约内部和外部访问。例如:
string public name = "Alice"
;
"private":
- 将函数或变量声明为私有的,只能在合约内部访问。例如:
uint256 private balance = 100
;
"external":
将函数声明为外部函数,只能从合约外部调用。与 "public" 关键字不同的是,外部函数不能在合约内部直接调用,也不能被合约继承。例如:
Copy Code function transfer(address recipient, uint256 amount) external { // transfer logic here }
"internal":
将函数声明为内部函数,只能在合约内部或合约继承链上的合约中访问。例如:
function withdraw(uint256 amount) internal { // withdraw logic here }
"payable":
将函数声明为可以接收以太、币的函数,在函数中可以接收以太币并进行转账操作。例如:
function purchase() public payable {
// purchase logic here}
函数
函数定义和调用:
- 定义函数并在需要时进行调用。 示例:
Copy Code
function sayHello(string memory name) public {// 函数逻辑// ...
}
sayHello("Alice");
函数可见性(public、private等):
- 可以限制函数的访问权限。 示例:
Copy Code
function myFunction() public {// 可公开访问的函数
}
function privateFunction() private {// 私有函数,只能在合约内部调用
}
函数修饰器(modifier):
-
用于修改函数的行为。 示例:
Copy Code modifier onlyOwner() { require(msg.sender == owner, "Only owner can call this function.");_; } function changeName(string memory newName) public onlyOwner {// 只有合约所有者可以调用该函数 name = newName; }
函数返回值:
- 可以定义函数的返回类型,并在函数结束时返回相应的值。 示例:
Copy Code
function add(uint256 a, uint256 b) public pure returns (uint256) {return a + b;
}
uint256 result = add(2, 3); // result = 5
数组
数组定义和初始化:
- 定义数组并指定元素个数或直接初始化数组。 示例:
uint256[] myArray; // 空数组uint256[] myArray2 = new uint256[](3); // 长度为3的动态数组uint256[] myArray3 = [1, 2, 3]; // 直接初始化数组
数组长度和访问元素:
-
可以使用
length
属性获取数组长度,并通过索引访问数组元素。 示例:uint256[] myArray = [1, 2, 3]; uint256 length = myArray.length; // length = 3uint256 secondElement = myArray[1]; // secondElement = 2
动态数组和静态数组:
- 动态数组的长度可以在运行时更改,而静态数组的长度在编译时确定。 示例:
uint256[] dynamicArray;
uint256[3] staticArray;
多维数组:
- 数组可以有多个维度,可以是二维、三维等。 示例:
uint256[][] twoDimensionalArray;
uint256[2][3] twoByThreeArray;
结构体
结构体定义和初始化:
- 定义结构体类型并初始化结构体变量。 示例:
struct Person {
string name;
uint age;
}
Person myPerson;
myPerson.name = "Alice";
myPerson.age = 25;
结构体成员访问:
- 可以通过结构体变量名和成员名访问结构体成员。 示例:
Person myPerson;
myPerson.name = "Alice";
string memory personName = myPerson.name; // personName = "Alice"
结构体作为函数参数和返回值:
- 可以将结构体作为函数的参数或返回值进行传递。 示例:
struct Person {
string name;
uint age;
}
function getPerson() public view returns (Person memory) {
Person memory person;
person.name = "Alice";
person.age = 25;
return person;
}
Person memory myPerson = getPerson();
结构体数组:
-
结构体可以组成数组,并通过索引访问数组元素。 示例:
struct Person { string name; uint age; } Person[] people; Person memory person1; person1.name = "Alice"; person1.age = 25; people.push(person1); Person memory person2; person2.name = "Bob"; person2.age = 30; people.push(person2);
储存方式
当在Solidity中声明变量时,可以使用不同的存储位置修饰符来指定变量应该存储在何处。共有三种存储位置: memory
、 storage
和 calldata
。其中, memory
和 storage
是最常用的两种。
memory
:
-
memory
是一种临时存储位置,用于存储函数执行期间的临时数据。它适用于需要在函数内部进行临时计算或处理大量数据的情况。在函数执行完毕后,memory
中的数据会被清空。 -
可以使用
memory
关键字将变量声明为memory
类型,也可以在函数参数中使用memory
。 示例:
function processArray(uint256 memory myArray) public {// 在 memory 中处理数组// ... }
storage
:
-
storage
是一种永久性存储位置,用于在合约的存储空间中存储和访问数据。它适用于需要在不同函数之间共享和保留数据的情况。在合约中声明的state variables
默认是storage
类型。 -
可以直接在函数内部使用
storage
类型的变量,无需显式声明。 示例:
calldata
:
-
calldata
是一种特殊的存储位置,用于存储函数参数和外部函数调用的输入数据。calldata
中的数据是只读的,不能被修改。此存储位置适用于函数参数传递和与外部合约交互。 -
在函数参数中,默认情况下,所有的非
mapping
类型参数都被视为calldata
类型。 示例:
function processInputData(uint256 calldata inputData) external {// 处理输入数据
}
function callExternalContract(address externalContract, bytes calldata data) external {
(bool success, ) = externalContract.call(data);
require(success, "External contract call failed.");
}
总结一下:
-
memory
用于临时存储函数执行期间的数据,适用于临时计算或处理大量数据的情况; -
storage
用于永久性存储变量,适用于在不同函数之间共享和保留数据的情况; -
calldata
用于存储函数参数和外部函数调用的输入数据,是只读的。
堆栈(Stack):
-
Solidity 中的堆栈主要用于函数调用的内部状态维护。每当一个函数被调用时,它会在堆栈上创建一个新的帧,该帧包含了这个函数的参数、局部变量、返回地址等信息。当函数执行完毕后,该帧将从堆栈中弹出。
-
通常情况下,开发者不需要手动操作堆栈,Solidity 编译器会自动进行堆栈管理。但在一些需要优化调用栈空间的场景下,可能需要手动控制函数调用堆栈的大小和顺序。 示例:
function foo(uint256 x, uint256 y) public pure returns (uint256) { uint256 result = bar(x) + y;return result; } function bar(uint256 x) public pure returns (uint256) { uint256 result = x * 2;return result; }
日志(Logs):
-
Solidity 中的日志主要用于记录合约内部的事件,如状态变更、交易处理等。通过日志,可以在区块链上查看合约的历史状态变化,并进行事件通知和监听。
-
日志由合约的事件(event)和事件参数组成,可以通过
emit
关键字触发。日志数据会被写入到交易的日志中,不会影响合约状态,但会占用一定的 Gas。 示例:
event Transfer(address indexed from, address indexed to, uint256 value);
function transfer(address _to, uint256 _value) public {
require(balances[msg.sender] >= _value, "Insufficient balance.");
balances[msg.sender] -= _value;
balances[_to] += _value;
emit Transfer(msg.sender, _to, _value);
}
Code
Solidity 中的一种特殊的数据类型,用于存储合约的字节码。
在 Solidity 中,合约代码(也称为字节码)可以通过 type
关键字将其存储在 bytes
或 bytescode
类型的变量中。这样可以在合约内部或外部对代码进行处理和分析。
以下是一个简单的示例,展示了如何将合约代码存储在 bytes
类型的变量中:
pragma solidity ^0.8.0;
contract CodeExample {
bytes public contractCode;
constructor() {
// 将合约代码存储在 contractCode 变量中
contractCode = type(CodeExample).creationCode;
}
}
Mappings
映射定义和初始化:
- 定义映射类型,并通过键值对存储和访问数据。 示例:
mapping(address => uint256) balances
;
映射的键值对:
- 映射由键值对组成,通过键来访问对应的值。 示例:
mapping(address => uint256) balances;
address account = 0xAbcdef1234567890; balances[account] = 100;
映射的访问和修改:
- 可以通过键来访问或修改映射中的值。 示例:
mapping(address => uint256) balances;
address account = 0xAbcdef1234567890; uint256 balance = balances[account]; // 获取映射值 balances[account] = 200; // 修改映射值
映射的迭代:
- Solidity 中的映射不支持直接迭代,需要结合其他数据结构或编写逻辑来实现迭代功能。
在 Solidity 中,映射(Mapping)是一种键值对的数据结构,类似于字典或哈希表。每个键对应一个唯一的值。但是,Solidity 中的映射并不支持直接迭代,这意味着你无法像遍历数组或列表那样直接对映射进行循环迭代。