引用類型詳解:數組、結構體、映射、字符串
學習目標
掌握 Solidity 中四種引用類型(數組、結構體、映射、字符串/變長字節數組)的定義和使用方法。
前置知識
已學習值類型(整型、布爾、地址、定長字節數組等)。
數組(Array)
storage 中的數組
Solidity 中數組分為兩種:
- 靜態數組
T[K]:長度固定,編譯時確定 - 動態數組
T[]:長度可變,運行時動態增減
關鍵規則:
push()/pop()只能操作 storage 中的動態數組public數組自動生成的 getter 函數,參數是下標(index)- 數組元素可以是任何類型,包括 struct 和 mapping
memory 中的數組
- 必須用
new關鍵字初始化,且長度在創建時確定 - 創建後長度固定,不能使用 push / pop
- 適用於函數內部的臨時計算
完整示例
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;
contract ArrayType {
uint8[3] data; // storage 靜態數組
uint8[] ddata; // storage 動態數組
// 返回靜態數組(拷貝到 memory 返回)
function testStaticArray() public view returns(uint8[3] memory) {
return data;
}
// 讀取動態數組
function testReadDynamicArray() public view returns(uint8[] memory) {
return ddata;
}
// 動態數組寫操作
function testWriteDynamicArray() public {
ddata.push(12);
ddata.pop();
ddata.push(90);
}
// 內存中的動態數組
function testMemoryDynamicArray(uint8 size) public pure returns(uint8[] memory) {
uint8[] memory mdata = new uint8[](size); // 必須初始化,不能 push/pop
return mdata;
}
}
結構體(Struct)
基本概念
結構體用來自定義數據類型,與 contract、enum 類似,是一種用戶定義類型。
特點:
- 可作為狀態變量、局部變量、函數參數和返回值
- 可放在 mapping 和數組中
- 成員可以是 mapping 或數組
基礎示例
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;
contract StructType {
struct Person {
string name;
uint8 age;
}
Person master;
function readPerson() public view returns(Person memory) {
return master;
}
function writePerson(Person memory p) public {
master = p;
}
function writePersonName(string memory name) public {
master.name = name;
}
// memory 中的 struct 不需要 new
function testMemoryStruct() public pure returns(Person memory) {
Person memory p;
p.name = "zhangsan";
p.age = 25;
return p;
}
// storage 局部變量指向成員變量的數據塊
function testStorageLocalStruct() public view returns(Person memory) {
Person storage p = master;
// 修改 p 會直接修改 master!
return p;
}
}
進階示例:結構體與數組、映射的組合
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
contract Structs {
struct Car {
string model;
uint year;
address owner;
}
Car public car;
Car[] public cars;
mapping(address => Car[]) public carsByOwner;
function examples() external {
// 三種初始化方式
Car memory toyota = Car("Toyota", 1990, msg.sender);
Car memory lambo = Car({year: 1980, model: "Lamborghini", owner: msg.sender});
Car memory tesla;
tesla.model = "Tesla";
tesla.year = 2010;
tesla.owner = msg.sender;
cars.push(toyota);
cars.push(lambo);
cars.push(tesla);
carsByOwner[msg.sender].push(toyota);
cars.push(Car("Ferrari", 2020, msg.sender));
// storage 引用可以修改原始數據
Car storage _car = cars[0];
_car.year = 1999;
// 刪除字段(重置為默認值)
delete _car.owner;
delete cars[1];
}
}
注意:
delete不會從數組中移除元素,而是將對應位置重置為默認值(零值)。數組長度不變。
映射(Mapping)
聲明語法
mapping(keyType => valueType) visibility variableName;
類型約束
- keyType:任何基本類型(
uint、address、bool、bytes、string等),不能是用戶自定義的複雜類型(struct、mapping、數組) - valueType:任何類型,包括 mapping(嵌套映射)
使用限制
- 只能作為狀態變量、storage 局部變量、庫函數參數
- 不能作為 public 函數的參數和返回值
public的 mapping 自動生成 getter 函數(以 key 作為參數)- 無法遍歷(由於 storage layout 的設計,key 經過哈希後分散存儲)
示例
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;
contract MappingType {
mapping(string => uint8) public ages;
function getAge(string memory name) public view returns(uint8) {
return ages[name];
}
function setAge(string memory name, uint8 age) public {
ages[name] = age;
}
}
提示:訪問 mapping 中不存在的 key 不會報錯,而是返回 valueType 的默認值(如
uint返回 0,address返回0x0)。
變長字節數組與字符串
bytes 與 string
bytes 和 string 都是引用類型(不是值類型),它們的底層存儲結構類似動態數組。
關鍵區別:
string是 UTF-8 編碼的字節數組,不可通過索引訪問單個字符bytes是原始字節數組,可以通過索引訪問- Solidity 沒有內置字符串操作函數(拼接、截取等需要借助庫或轉為
bytes操作) string和bytes之間可以互相轉換(不拷貝數據)
使用規則
| 場景 | 推薦類型 |
|---|---|
| 任意長度的原始數據 | bytes |
| 任意長度的字符串(文本) | string |
| 固定長度的數據(1~32 字節) | bytes1 ~ bytes32(值類型,更省 gas) |
示例
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;
contract BytesAndString {
string name = "BytesAndString";
bytes name1 = "BytesAndString1";
function testStringAndBytes() public view returns(string memory) {
string memory data = "xyz";
bytes memory data1 = "abc";
// storage 到 memory 的拷貝
data = name;
data1 = name1;
// 類型轉換
data1 = bytes(data);
data = string(data1);
return data;
}
}
小結
| 類型 | 關鍵特點 |
|---|---|
| 數組 | storage 可動態增減,memory 定長;push/pop 僅限 storage 動態數組 |
| 結構體 | 自定義復合類型;memory 中無需 new;storage 局部變量是引用 |
| 映射 | 鍵值對存儲;不可遍歷;不能作為函數參數/返回值 |
| string/bytes | 引用類型;string 不可索引;固定長度優先用 bytesN 節省 gas |
下一篇:存儲位置與拷貝機制 —— 理解 storage、memory、calldata 的區別及引用類型的拷貝規則。
主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://walker-learn.xyz/archives/7489