文档首页> Linux命令> 【从0学习Solidity】变量初始值

【从0学习Solidity】变量初始值

发布时间:2025-08-29 00:10       

下面这一篇把 Solidity 中“变量初始值” 讲清楚:哪些会被自动置零、哪些必须先赋值、delete 如何复位,以及数组/映射/结构体/枚举的细节。适合从 0 起步的你一次掌握 ✅。


一句话概念

在 Solidity(0.8.x 主线)里:状态变量(storage)默认零值;函数内局部变量不能读未赋值;用 new 创建的 memory 动态数组会被零填充;delete 会把目标还原到其类型的默认值。🙂


规则总览表(精炼版)

场景/类型 初始值/行为 说明
uint / int(storage) 0 有符号/无符号皆为 0
bool(storage) false 布尔默认假
address/address payable address(0)/payable(address(0)) 全 0 地址
bytes32/定长 bytesN 全 0 定长字节均置零
string/ 动态 bytes(storage) 空串 / 长度 0 不占用额外数据
固定长度数组(storage) 每个元素置零 例:uint[3]→[0,0,0]
动态数组(storage) 长度 0 未 push 之前为空
mapping 任意 key 返回“值类型默认值” 读不存在 key 得默认值;不存在“键集合”
struct(storage) 各字段置零 递归地零初始化
enum(storage) 枚举下标 0(第一个成员) 如 enum S{A,B}默认 S.A
局部标量(函数内) 不能读未赋值 编译时报错
new T[](memory) 元素全为 0 内存数组被零填充
calldata参数 由外部调用提供 无“默认值”概念
delete x 还原为类型默认值 复位标量/数组/struct;delete m[k]仅清该键

必知细节与推理

  1. 为何状态变量会“自动为 0”?
    EVM 语义:未写入的存储槽读取为 0。Solidity 的状态变量落在 storage 槽里,因此天然零初始化。这对结构体、固定数组是递归生效的,对动态数组则体现为长度 0。
  2. 为何函数里的局部变量必须先赋值?
    为避免把“随机内存值”误当数据,编译器强制:局部变量读之前必须写。所以 uint x; return x; 会直接编译失败。🚫
  3. memory 动态数组如何初始化?
    使用 new uint[](n) 时,编译器会零填充分配到的内存段(与 EVM 内存分配策略一致),因此每个元素起始为 0。
  4. mapping 为何读不到“未设置”和“真的为 0”的区别?
    读不存在的键返回值类型默认值(如 0/false/空结构),所以需通过配套布尔位或外部索引区分“是否曾写入”。
  5. enum 默认值与安全性
    默认是第一个枚举成员;若用底层写存储可能产生无效枚举值,读写时应做范围校验(常用于防御性编程)。
  6. delete 的一致复位语义
    • delete x:把 x 设为其类型默认值;
    • delete arr[i]:只复位该元素,不改变数组长度;
    • delete arr(动态数组):长度置 0;
    • delete m[k]:仅清除该键的值为默认值;不能 delete m(整个映射不可删除)。
  7. 常量与不可变量
    • constant:声明时必须赋值;
    • immutable:需在声明或构造函数内一次性赋值;二者不存在默认值概念。

可直接验证的示例(Solidity ^0.8.x)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract DefaultValues {
    enum Status { None, Active, Closed }
    struct User { uint id; bool ok; address owner; }

    // ---- storage 状态变量(自动零初始化)----
    uint public a;                 // 0
    bool public b;                 // false
    address public c;              // 0x000...0
    bytes32 public d;              // 0x0
    string public s;               // ""
    uint[3] public fixedArr;       // [0,0,0]
    uint[] public dynArr;          // length=0
    mapping(address => uint) public bal;  // 缺键返回 0
    Status public st;              // Status.None
    User public u;                 // {id:0, ok:false, owner:0}

    // ---- memory 动态数组被零填充 ----
    function sumOfZeros(uint n) external pure returns (uint) {
        uint[] memory arr = new uint[](n); // 全 0
        uint ssum;
        for (uint i; i < n; ) { ssum += arr[i]; unchecked{ ++i; } }
        return ssum; // 恒为 0
    }

    // ---- delete 复位为默认值 ----
    function resetA() external {
        a = 5;
        delete a; // a -> 0
    }

    // // 编译失败示例(请勿取消注释):
    // function bad() external pure returns (uint) {
    //     uint x;        // 未赋值
    //     return x;      // ❌ 编译错误:读未赋值的局部变量
    // }
}
​

要点解读

  • 所有 public 状态变量在部署后立刻可读