引用类型详解:数组、结构体、映射、字符串

引用类型详解:数组、结构体、映射、字符串

学习目标

掌握 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)

基本概念

结构体用来自定义数据类型,与 contractenum 类似,是一种用户定义类型。

特点:

  • 可作为状态变量、局部变量、函数参数和返回值
  • 可放在 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:任何基本类型(uintaddressboolbytesstring 等),不能是用户自定义的复杂类型(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

bytesstring 都是引用类型(不是值类型),它们的底层存储结构类似动态数组。

关键区别:

  • string 是 UTF-8 编码的字节数组,不可通过索引访问单个字符
  • bytes 是原始字节数组,可以通过索引访问
  • Solidity 没有内置字符串操作函数(拼接、截取等需要借助库或转为 bytes 操作)
  • stringbytes 之间可以互相转换(不拷贝数据)

使用规则

场景 推荐类型
任意长度的原始数据 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

(0)
Walker的头像Walker
上一篇 2026年3月8日 15:11
下一篇 14小时前

相关推荐

  • Web3 概述与愿景

    Web3 概述与愿景 学习目标 理解 Web1、Web2、Web3 的演进历程与核心区别 掌握 Web3 的核心理念:去中心化、数据确权、用户主权 了解 Web3 带来的创新机会与全新商业模式 熟悉 Web3 开发者的学习路线图 前置知识 基本的互联网使用经验 对软件开发有初步了解(非必须,但有助于理解技术部分) 一、Web1 → Web2 → Web3 的…

    Web3与WASM 2026年3月10日
    6200
  • 继承多态与库合约

    继承多态与库合约 学习目标 掌握 Solidity 继承机制与多态 理解 C3 线性化算法 掌握库合约(library)的定义和使用 继承基础 继承定义 使用 is 关键字 继承的实现方式是代码拷贝:部署后变成一个合约 可见性与继承 private:子合约不可见,但不能定义同名成员 internal:子合约可见 public:完全可见 event 和 mod…

    Web3与WASM 2026年3月10日
    5400
  • Solidity 入门与开发环境

    Solidity 入门与开发环境 学习目标 理解智能合约的本质与核心特性 掌握合约在以太坊上的运行原理(Transaction + EVM) 认识 Solidity 语言特点与开发工具链 编写并部署第一个智能合约 前置知识 了解区块链基本概念(区块、交易、共识) 了解以太坊账户模型(EOA 与合约账户) 基本编程经验(任意语言均可) 一、智能合约的根本性质 …

    2026年3月10日
    10100
  • 函数定义与访问控制

    函数定义与访问控制 学习目标 掌握 Solidity 函数定义、可见性修饰符、交易属性、modifier 和构造函数。 函数定义 一般形式 function fname([参数]) [可见性][交易属性][modifier...] returns(返回值) { ... } 函数签名:fname([参数]) —— 唯一标识一个函数 返回值:returns(返回…

    Web3与WASM 2026年3月10日
    5500
  • 动态调用与Fallback机制

    动态调用与Fallback机制 学习目标 掌握 call 动态调用的语法与使用场景 理解 calldata 数据结构(selector + 参数编码) 掌握 fallback / receive 函数的触发机制 理解 tx、msg、block 三种上下文变量的区别 call 动态调用 基本语法 (bool success, bytes memory data…

    2026年3月10日
    6000
简体中文 繁体中文 English