WTF-Solidity 入门第八讲:Solidity 变量初始值全解析 —— 默认值与delete操作符实战指南
【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程,供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity
本讲内容基于 WTF-Solidity 极简入门教程第 8 讲(中文原版 08_InitialValue/readme.md 及西班牙语版 Languages/es/08_ValorInicial_es/readme.md)编写。在 Solidity 中,声明但未赋值的变量会自动获得其类型的"初始值"(默认值),理解这一机制是写出安全、可预期合约的基础。读完本讲,你将完整掌握值类型、引用类型变量的默认初始值规则,并能熟练使用
delete操作符将任意变量一键恢复为初始状态,同时学会在 Remix 中通过public变量的getter函数逐项验证这些行为。
一、什么是变量初始值
在 Solidity 中,当你声明一个变量却没有给它赋值时,编译器并不会让它处于"未定义"状态,而是会为它赋予一个与该类型对应的默认初始值。这一点与许多高级语言(如 JavaScript 中未初始化变量为undefined)不同:Solidity 中的变量永远有确定的值。
从 EVM 的底层视角看,合约的存储(storage)槽位默认是清零的,因此凡是被声明而未显式赋值的状态变量,最终都会收敛到"该类型所表示的 0"——布尔值就是false、数值就是0、地址就是零地址address(0)。本讲后续介绍的所有初始值,本质上都是"0 在该类型中的表现形式"。
二、值类型变量的初始值
Solidity 中各类值类型变量的默认初始值如下表所示:
| 类型 | 默认初始值 | 说明 |
|---|---|---|
boolean | false | 布尔假 |
string | "" | 空字符串 |
int | 0 | 有符号整数 0 |
uint | 0 | 无符号整数 0 |
enum | 枚举中的第一个元素 | 即索引0对应的枚举项 |
address | 0x0000000000000000000000000000000000000000 | 即address(0),零地址 |
function(internal) | 空白函数 | 不执行任何逻辑的函数 |
function(external) | 空白函数 | 不执行任何逻辑的函数 |
对应的合约代码(摘自 08_InitialValue/InitialValue.sol)如下:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.34; contract InitialValue { // 值类型 bool public _bool; // false string public _string; // "" int public _int; // 0 uint public _uint; // 0 address public _address; // 0x0000000000000000000000000000000000000000 enum ActionSet { Buy, Hold, Sell} ActionSet public _enum; // 第一个元素,即索引 0 的 Buy function fi() internal{} // internal 空白函数 function fe() external{} // external 空白函数 }几个值得注意的细节:
- 枚举的初始值:枚举类型
ActionSet { Buy, Hold, Sell }声明后未赋值时,其默认值是第一个元素Buy,底层对应索引0。也就是说,枚举的初始值不是"随机一个成员",而是始终收敛到0号成员。 - 函数的初始值:无论是
internal还是external的函数类型变量,初始值都是一个空白函数(empty function),调用它不会有任何实际行为。这也是"0 值"在函数类型上的体现。 - 验证方式:由于上述变量都被声明为
public,Solidity 会自动为它们生成getter函数,你可以直接在 Remix 的部署面板中点击对应按钮,读取并确认每个变量的初始值。
三、引用类型变量的初始值
引用类型(mapping、struct、array)的初始值规则可以归纳为一句话:其内部所有成员/元素都被设置为各自类型的默认值。具体表现如下:
| 类型 | 默认初始值 | 说明 |
|---|---|---|
mapping | 所有元素均为其键/值类型的默认值 | 例如mapping(uint => address)的每个槽位值都是address(0) |
struct | 所有成员均设为其类型的默认值 | 例如含两个uint256成员的 struct,初始为(0, 0) |
动态数组uint[] | [] | 空数组,长度为 0 |
静态数组uint[8] | [0, 0, 0, 0, 0, 0, 0, 0] | 所有元素均为元素类型的默认值 |
对应的合约代码(继续阅读 08_InitialValue/InitialValue.sol):
// 引用类型 uint[8] public _staticArray; // 所有成员设为其默认值的静态数组 [0,0,0,0,0,0,0,0] uint[] public _dynamicArray; // `[]` mapping(uint => address) public _mapping; // 所有元素都为其默认值的 mapping // 所有成员设为其默认值的结构体 0, 0 struct Student{ uint256 id; uint256 score; } Student public student;要点说明:
mapping无法遍历且不可见"全部元素",它的初始值体现在"任何键查询到的值都是该值类型的默认值"——例如上例中_mapping(0)、_mapping(1)查询结果均为0x0000...0000。- 静态数组与动态数组的初始值不同:动态数组初始长度为
0(空数组[]),而静态数组的长度在声明时已固定(如uint[8]固定为 8 个元素),其初始值为 8 个0。 struct的初始值逐成员递归:Student的id和score两个uint256成员初始都是0,即(0, 0)。
四、delete操作符:一键恢复初始值
delete是 Solidity 提供的一个特殊操作符,语法为delete a,其作用是把变量a的当前值替换为其类型的初始值。可以把它理解为"重置变量"。
合约示例(摘自 08_InitialValue/InitialValue.sol):
// delete 操作符 bool public _bool2 = true; function d() external { delete _bool2; // delete 会让 _bool2 变为默认值,即 false }上例中,_bool2在声明时被显式赋值为true,而一旦调用d()执行delete _bool2,它的值就会被重置为false。
delete对不同类型的重置效果归纳如下:
| 操作 | 效果 |
|---|---|
delete布尔/数值/地址 | 重置为false/0/address(0) |
delete动态数组 | 长度变为0,成为空数组[] |
delete静态数组 | 所有元素恢复为元素类型的默认值 |
delete结构体 | 所有成员恢复为各自类型的默认值 |
delete枚举 | 重置为第一个枚举元素(索引 0) |
delete映射 | 映射中已存在的键值对被移除(恢复到未写入的默认状态) |
安全提示:delete是一个会真实改变链上状态的写操作,调用时需考虑 gas 成本(尤其是对大数据量结构体或数组执行delete时,需要为逐槽位清零支付存储 gas);同时应结合本仓库安全专题(如 S01_ReentrancyAttack)的教训,在涉及状态重置的业务逻辑中做好访问控制与重入防护。
五、在 Remix 上验证:实战步骤
下面在 Remix IDE 中部署合约,逐步验证上述初始值与delete行为。完整的可运行源码见 08_InitialValue/InitialValue.sol(西班牙语注释版见 Languages/es/08_ValorInicial_es/InitialValue.sol)。
步骤 1:编译并部署合约打开 Remix,新建文件InitialValue.sol并粘贴上述完整合约代码,选择^0.8.34及以上版本的编译器完成编译,然后在"部署与运行"面板中部署InitialValue合约。
步骤 2:查看各变量的初始值部署完成后,面板会为每个public变量自动生成对应的getter按钮。逐个点击即可读取到声明但未赋值变量的初始值——值类型(_bool、_string、_int、_uint、_address、_enum)与引用类型(_staticArray、_dynamicArray、_mapping、student)均显示为上文表格中的默认值:
步骤 3:调用delete并验证重置效果点击d函数按钮(或按上图中各delete对应函数)执行delete _bool2,随后再次读取_bool2,可以看到它从true被重置为false;若预先对_staticArray、student等写入数据,执行delete后它们同样会恢复到各自的默认初始值:
通过以上三个步骤,你可以直观地确认:"声明未赋值 → 默认初始值"与"delete → 恢复初始值"这两条规则对值类型和引用类型都成立。
六、仓库源码与多语言对照
本讲内容在仓库中有完整的中、英、西等多语言实现,便于对照学习:
- 中文版:源码 08_InitialValue/InitialValue.sol、教程 08_InitialValue/readme.md;另在 src/08_InitialValue 目录下也保留了该讲的镜像副本。
- 西班牙语版:源码 Languages/es/08_ValorInicial_es/InitialValue.sol、教程 Languages/es/08_ValorInicial_es/readme.md。
- 英语版:见 Languages/en/08_InitialValue_en。
三份源码在合约逻辑上完全一致,仅在注释语言上有所不同,读者可以对照阅读,同时巩固 Solidity 术语的双语表达。教程体系方面,本讲属于 WTF-Solidity 基础部分,建议结合前序的 07_Mapping(映射)与 06_ArrayAndStruct(数组与结构体)一起学习,以理解引用类型的存储行为。
七、小结
本讲围绕"变量初始值"这一 Solidity 基础但关键的概念,梳理了完整规则:
- 值类型:
bool→false,string→"",int/uint→0,enum→ 第一个元素(索引 0),address→address(0),函数类型 → 空白函数。 - 引用类型:
mapping→ 所有元素均为默认值,struct→ 所有成员均为默认值,动态数组 →[],静态数组 → 所有元素均为默认值。 - 核心结论:变量被声明但没有赋值时,其值默认为该类型的初始值,本质上是"0 在对应类型中的表现形式"。
delete操作符:delete a可以把变量a的值删除并恢复为初始值,是合约中常用的状态重置手段。
掌握这些默认值规则,能帮助你在阅读他人合约、编写初始化逻辑以及排查"变量为什么是这个值"的问题时更加得心应手,也是后续学习存储布局、代理合约升级等高级主题的必备基础。
【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程,供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考