Solidity 数据类型小结
| 分类 | 核心内容 | 关键特性 |
|---|---|---|
| 类型体系 | 分为值类型、引用类型、映射类型三大类 | Solidity 是静态类型语言,变量类型编译时确定 |
| 值类型 | 包含布尔、整型(uint/int)、地址、定长字节数组、枚举、函数类型等 | 1. 赋值/传参时值拷贝,修改副本不影响原变量 2. 数据占用 ≤32 字节 3. 示例: uint a=10; uint b=a; b=20; → a 仍为 10 |
| 引用类型 | 包含数组、结构体、string/bytes(动态字节数组)、合约 | 1. 数据占用通常 >32 字节,支持引用传递减少拷贝开销 2. 必须显式指定数据位置(memory/storage/calldata) 3. 赋值规则: - 不同位置赋值 → 拷贝副本 - 相同位置赋值 → 创建引用(修改新变量会影响原变量) |
| 数据位置(引用类型专属) | 4 种位置(transient 不支持引用类型) | |
| storage | 存储状态变量,永久保存在区块链,Gas 消耗最高 | |
| memory | 函数内临时存储,函数执行后释放,Gas 中等 | |
| calldata | 存储 external 函数参数,只读不可修改,Gas 消耗最低 | |
| transient | 单次交易内有效,不支持引用类型定义 | |
| 映射类型 | 键值对结构(mapping(KeyType => ValueType)) | 1. 类似字典/哈希表,仅支持作为状态变量(storage) 2. 详细用法需结合后续数据结构章节学习 |
值类型 vs 引用类型
- 值类型:拷贝传递,独立存储,适合简单数据。
- 引用类型:需指定数据位置,支持引用传递,适合复杂数据(数组、结构体等),可节省 Gas。
数据位置选择原则
- 状态变量默认
storage; - 函数内临时变量用
memory; external函数参数优先用calldata;- 交易内临时共享数据用
transient(仅限值类型)。
- 状态变量默认
赋值行为关键规则
不同位置赋值→拷贝,相同位置赋值→引用。
Solidity 整型(uint与int)小结
| 类别 | 核心内容 |
|---|---|
| 类型定义 | 1. 两类整型:uint(无符号整数,非负)、int(有符号整数,可正负)2. 位数范围: uint8-uint256、int8-int256,8位步进3. 默认类型: uint等价uint256,int等价int256 |
| 取值范围 | 1. uintN:0 ~ 2^N - 1(如uint8:0~255)2. intN:-2^(N-1) ~ 2^(N-1) - 1(如int8:-128~127)3. 默认值:所有整型变量未赋值时默认0 |
| 运算规则 | 1. 支持运算符:比较(>/</==等)、算术(+/-/*///%/**)、位运算(&/` |
| Gas 优化 | 确定运算无溢出时,使用unchecked模式可节省 Gas(单次约省0.26%,循环场景效果显著) |
| 使用场景选择 | 1. 优先用 uint:代币余额、数组索引、时间戳、计数器等非负场景 2. 使用 int:温度、价格波动、数值差值等需表示正负的场景 |
| 常见陷阱&避坑方案 | 1. 无符号下溢:uint x=0; x-1会回退 → 先判断x>0再减法2. 除法截断:需精度时放大分子(如 (5*1e18)/2)3. 类型转换截断:大类型转小类型前,用 require(val <= type(uint8).max)校验4. 跨类型比较: int与uint直接比较易出错 → 统一转换为相同类型再比较 |
| 进阶技巧 | 用type(X).min/type(X).max获取整型X的最值(如type(uint8).max=255) |
Solidity 布尔类型小结
| 分类 | 核心内容 | 关键说明/示例 |
|---|---|---|
| 基础特性 | 关键字、取值、默认值 | 关键字:bool;取值:true/false;默认值:false |
| 核心运算符 | 逻辑运算符:&&(与)、` | |
| 短路求值 | 规则、优化技巧 | 规则:&&左false/` |
| 典型应用 | 权限控制、状态管理、功能开关、条件判断 | 示例: 1. 权限:`user == owner |
| 最佳实践 | 命名、Gas优化、安全校验 | 1. 命名:isXXX/hasXXX;2. Gas:bool与小类型打包存储; 3. 校验: require(布尔条件, 报错信息) |
Solidity 地址类型小结
| 分类 | 核心内容 | 关键说明/示例 |
|---|---|---|
| 基础定义 | 地址类型作用、两种类型 | 作用:表示以太坊账户/合约地址(20字节) 类型: 1. address:普通地址,不可接收ETH2. address payable:可支付地址,可接收ETH |
| 类型转换 | 普通地址转可支付地址 | 语法:address payable ap = payable(addr);注意:合约地址需实现 receive/fallback才能接收ETH |
| 常用操作 | 1. 地址比较 2. 查询余额 3. ETH转账 | 1. 比较:==/!=,零地址:address(0)2. 余额: addr.balance(单位wei,address(this).balance查合约余额)3. 转账:推荐 to.call{value: msg.value}(""),需校验返回值 |
| 重要提示 | 开发最佳实践与风险防范 | 1. 校验零地址:require(addr != address(0))2. 转账优先用 call,避免transfer/send的2300 Gas限制3. 防范重入攻击 |
| |
Solidity 合约类型总结
| 核心维度 | 具体内容 |
|---|---|
| 合约类型定义 | Solidity 中合约本身是一种数据类型,与 uint、address 等基础类型地位等同,可声明合约类型变量(如 Hello h)。 |
| 合约创建方式 | 使用 new 关键字在合约中部署新合约(如 h = new Hello()),返回新合约实例。 |
| 合约交互方式 | 通过合约类型变量调用目标合约的公开函数(如 h.sayHi()),实现合约间函数调用。 |
| 类型转换规则 | 合约类型 ↔ 地址类型可双向转换: 1. 合约转地址: address(hello)2. 地址转合约: Hello(helloAddr) |
| 核心应用场景 | 构建复杂合约系统(如工厂合约创建子合约、多合约协作交互)。 |
| 关键注意事项 | 仅能调用目标合约的公开/外部函数;转换地址需确保对应合约已部署且类型匹配。 |
| |
Solidity 枚举类型总结
| 核心维度 | 具体内容 |
|---|---|
| 定义方式 | 使用 enum 关键字定义,格式:enum 枚举名 { 常量1, 常量2, ... } |
| 数值特性 | 1. 从 0 开始递增编号 2. 底层存储为 uint8 类型3. 默认值为第一个枚举值 |
| 类型转换 | 1. 枚举 ↔ uint 可相互转换 2. 整型转枚举需用 require 检查值范围,避免越界 |
| 核心操作 | 1. 支持 ==/!= 比较运算2. 通过 type(枚举名).min/max 获取取值范围 |
| 典型应用场景 | 表示有限状态集合:订单状态、任务工作流、合约运行状态等 |
| 最佳实践 | 1. 替代“魔法数字”提升代码可读性 2. 状态转换时添加合法性验证 3. 状态变更时触发事件 |
| 核心优势 | 1. 代码可读性/可维护性提升 2. 限制变量取值范围,减少非法状态 |
- 枚举是值类型,赋值时会进行拷贝,而非引用;
- 枚举定义后不可动态增删常量,适合表示固定的状态集合;
- 结合结构体、映射可实现复杂的状态管理(如订单/任务系统)。
Solidity 数组总结
| 核心维度 | 具体内容 |
|---|---|
| 数组类型分类 | 1. 固定长度数组:T[k](如 uint[10] tens),长度不可变,仅支持下标赋值;2. 动态长度数组: T[](如 uint[] numbers),长度可动态调整,支持 push/pop。 |
| 数据位置要求 | 数组属于引用类型,声明时需指定位置: - storage(状态变量默认):可动态扩展,支持 push/pop;- memory: new 创建后长度固定;- calldata:只读,支持数组切片。 |
| 初始化方式 | 1. 直接赋值:uint[] public u = [1,2,3];2. new 关键字:uint[] memory c = new uint(len)(内存数组需指定长度,存储数组可空)。 |
| 访问与操作 | 1. 下标访问:arr[index](序号从 0 开始),多维数组访问顺序与定义相反(如 uint[][5] x 访问 x[2][1]);2. public 数组自动生成访问器函数(参数为下标),返回整个数组需自定义函数; 3. 成员: - length:只读属性,内存数组长度固定;- push()/push(x):向动态数组末尾添加元素(仅 storage);- pop():删除末尾元素(仅 storage)。 |
| 特殊功能 | calldata 数组支持切片:x[start:end](start 默认 0,end 默认数组长度),常用于提取函数选择器(如 payload[:4])。 |
| 特殊数组类型 | 1. string:字符数组,不支持 push/pop,无内置字符串操作函数,Gas 效率低;2. bytes:动态字节数组,Gas 效率高于 byte[],支持 push/pop,可通过 bytes(s) 转换 string 后按 UTF-8 编码访问。 |
| 开发注意事项 | 1. 避免遍历大数组:易导致 Gas 超限,建议链下计算/分段处理; 2. 删除元素:优先用 pop() 或「最后元素替换+pop」减少 Gas 消耗;3. 短字节数组:优先用 bytes1-bytes32 降低 Gas。 |
- Solidity 数组分固定/动态长度,核心区别是是否支持
push/pop及长度能否调整; - 数据位置决定数组操作能力(storage 功能最全,calldata 支持切片,memory 长度固定);
- string/bytes 作为特殊数组,功能有限且需关注 Gas 效率,大数组遍历需规避 Gas 超限风险。
你希望我根据这份 Solidity 中 string 与 bytes 的学习资料,生成一份结构化的总结表格,清晰梳理各类字节/字符串类型的核心信息。
Solidity 字节与字符串类型总结
| 类型分类 | 具体类型 | 核心特性 | 主要使用场景 | Gas 效率 | 核心操作/限制 |
|---|---|---|---|---|---|
| 定长字节数组 | bytes1 ~ bytes32 | 1. 值类型,赋值/传参拷贝 2. 长度固定不可变 3. 索引只读访问 4. 占用空间固定 | 存储哈希值、ID、固定长度原始字节数据 | ⭐⭐⭐⭐⭐ | 1. 支持比较/位运算/移位运算 2. 可通过 .length 获取长度3. 索引仅可读,不可修改 |
| 动态字节数组 | bytes | 1. 引用类型(需指定 memory/storage/calldata) 2. 长度动态可变 3. 可修改元素 | 存储任意长度的原始字节数据 | ⭐⭐⭐⭐ | 1. 支持 push()/pop() 增删元素 2. 可通过索引修改值 3. bytes.concat() 拼接 4. 比 bytes1[] 更省 Gas |
| 动态字节数组 | bytes1[] | 1. 动态数组,元素为 bytes1 2. 存储结构松散 | 几乎不推荐使用(仅兼容旧代码) | ⭐⭐ | 操作同普通数组,但 Gas 消耗高 |
| 字符串类型 | string | 1. 引用类型,UTF-8 编码 2. 动态长度 3. 原生操作受限 | 存储文本数据(用户名、描述、NFT 的 URI 等) | ⭐⭐⭐ | 1. 无 length 属性、不支持索引访问 2. 比较需通过 keccak256 哈希 3. 复杂操作需先转 bytes |
| 类别 | 核心内容 |
|---|---|
| 常用类型转换 | 1. string ↔ bytes:直接强转(bytes(str)/string(bytesData)) 2. string ↔ bytes32:需处理长度(字符串≤32字节),bytes32 转 string 要剔除尾部零字节 |
| Gas 优化建议 | 1. 固定长度数据优先用 bytes32(如哈希、短ID) 2. 变长原始数据用 bytes 而非 bytes1[] 3. 长文本数据链下存储(IPFS),合约仅存哈希/URI 4. 短字符串(≤32字节)可考虑用 bytes32 替代 string |
- 类型选择核心:固定长度数据选
bytes1~bytes32(优先 bytes32),变长原始字节选bytes,文本数据选string,避免使用bytes1[]。 - 操作核心:
string的复杂操作(长度、索引、修改)需先转为bytes,字符串比较需通过哈希值而非直接==。 - Gas 优化核心:优先选择存储更紧凑的类型(bytes32 > bytes > string > bytes1[]),长文本数据尽量链下存储。
Solidity 结构体总结
| 类别 | 具体说明 | 示例/注意事项 |
|---|---|---|
| 结构体定义 | 使用 struct 关键字定义自定义复合类型,将多个不同类型数据组合成新类型 | struct Person { address account; bool gender; uint8 age; } |
| 成员类型规则 | 1. 支持基本类型、数组、映射、其他结构体作为成员 2. 不能直接包含自身作为成员 3. 可包含自身类型的数组/映射(不推荐) | 错误示例:struct Person { Person child; }(无法编译)合法示例: struct Person { Person[] childs; } |
| 赋值方式 | 1. 仅声明(使用默认值) 2. 按成员顺序赋值 3. 具名方式赋值 4. 逐个更新成员 | 具名赋值示例:Person({account: address(0x0), gender: false, age: 18}) |
| 访问器函数 | public 结构体状态变量会自动生成访问器函数,无参数,返回包含所有成员的元组 | 生成的函数示例:function person() external view returns (address, bool, uint8) { ... } |
| 存储特性 | 结构体成员默认连续分配存储槽;变长成员(数组/映射)单独分配存储槽,仅留指向位置 | 解释了“可包含自身数组/映射,但不能直接包含自身”的底层原因 |
Solidity 映射知识点总结表格
| 核心知识点 | 具体内容 |
|---|---|
| 映射定义方式 | 键值对存储结构,格式:mapping(KeyType => ValueType),功能类似 Java Map/Python Dict,常用于存储余额、用户等级等键值数据 |
| 存储位置限制 | 仅能保存在 storage(状态变量默认),不支持 memory 修饰 |
| 键/值类型限制 | 键:仅支持内置值类型、bytes、string、合约/枚举,不支持映射、变长数组、结构体等复杂类型 值:无类型限制,可是任意类型(包括嵌套映射) |
| 核心特性 | 1. 无长度属性,无法直接获取长度 2. 无键/值集合,无法直接迭代 3. 不存在的键返回对应值类型默认值(如 uint 为 0,bool 为 false) |
| 嵌套映射 | 支持嵌套(值类型为另一个映射),示例:mapping(address => mapping(address => uint)) tokenBalances |
| 访问器函数 | public 映射状态变量会生成访问器函数,参数为键类型,返回值类型;嵌套映射的访问器会接收多层键参数 |
| 可迭代映射实现方式 | 方式1:维护键的数组(实现简单但Gas成本较高) 方式2:通过链表(推荐,可实现O(1)复杂度),用mapping存储下一个元素地址 |
| 数组 vs 映射 | 数组:适合迭代场景,Gas消耗随长度增加变大,不宜过大 映射:适合已知键快速取值,Gas消耗更优,不支持直接迭代 |
Solidity 函数总结
| 模块 | 核心分类/概念 | 关键细节 | 示例/注意事项 |
|---|---|---|---|
| 函数基本语法 | 完整结构 | function 函数名(参数类型 参数名) 可见性 状态可变性 [修饰器] returns (返回值类型) { 函数体 } | 严格遵循语法结构,提升代码可读性 |
| 函数可见性 | public | 当前合约、子合约、外部均可调用;状态变量自动生成同名 getter 函数 | 内外部都需要调用的函数 |
external | 仅外部可调用;内部需通过 this 调用 | 只需外部调用的函数(更省 gas) | |
internal | 当前合约、子合约可调用;外部不可调用 | 合约内部/继承的辅助函数(命名建议加前缀 _) | |
private | 仅当前合约可调用;子合约不可访问 | 当前合约私有实现细节 | |
| 选择原则 | 优先最严格可见性(private→internal→external→public),减少攻击面 | 避免不必要的 public,辅助函数用 internal/private | |
| 状态可变性 | 无修饰符 | 可读写状态,不可接收 ETH | 普通状态修改(如赋值状态变量) |
view | 仅读取状态,不可修改 | 查询数据(如读取状态变量),外部调用免费 | |
pure | 不读写状态,仅依赖入参计算 | 纯数学运算(如加法、乘法) | |
payable | 可读写状态,可接收 ETH | 转账、存款等需要接收 ETH 的场景 | |
| 选择原则 | 不修改状态的函数必须标记 view/pure,编译器强制校验 | 避免省略状态可变性修饰符,提升安全性和可读性 | |
| 函数调用方式 | 内部调用 | 直接用函数名调用;同一执行上下文;msg.sender/msg.value 不变;gas 消耗低 | 合约内部函数间调用,优先使用 |
| 外部调用 | 通过 this/合约实例调用;新执行上下文;msg.sender 可能变化;gas 消耗高 | 调用外部合约函数,需注意重入攻击风险 | |
| 参数数据位置 | memory | 临时存储,函数调用后释放;可读写 | 函数参数/局部变量(如字符串、数组) |
calldata | 只读,外部函数参数专用;最省 gas | external 函数的数组/字符串参数 | |
storage | 永久存储,引用状态变量;gas 消耗高 | 内部函数中引用状态变量(如结构体、数组) | |
| 函数重载 | 规则 | 同名函数,参数列表(个数/类型)不同;仅返回值不同无法重载 | 实现同一功能的多参数版本(如两数相加/三数相加) |
| 特殊函数 | constructor | 合约部署时执行一次;可标记 payable(部署时接收 ETH) | 初始化状态变量(如设置合约 owner、token 名称) |
receive() | 接收纯 ETH 转账时触发;必须 external payable,无参数无返回值 | 处理用户直接转账 ETH 的逻辑 | |
fallback() | 调用不存在的函数/带数据的转账时触发;必须 external payable | 处理未知调用,兜底逻辑 | |
| 多返回值 | 语法 | 支持多值返回;可命名返回值;支持解构赋值(忽略部分值用 ,) | 一次性返回多个相关数据(如数字、布尔值、字符串) |