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. 详细用法需结合后续数据结构章节学习
  1. 值类型 vs 引用类型

    • 值类型:拷贝传递,独立存储,适合简单数据。
    • 引用类型:需指定数据位置,支持引用传递,适合复杂数据(数组、结构体等),可节省 Gas。
  2. 数据位置选择原则

    • 状态变量默认 storage
    • 函数内临时变量用 memory
    • external 函数参数优先用 calldata
    • 交易内临时共享数据用 transient(仅限值类型)。
  3. 赋值行为关键规则

    不同位置赋值→拷贝,相同位置赋值→引用。

Solidity 整型(uint与int)小结

类别核心内容
类型定义1. 两类整型:uint(无符号整数,非负)、int(有符号整数,可正负)
2. 位数范围:uint8-uint256int8-int2568位步进
3. 默认类型:uint等价uint256int等价int256
取值范围1. uintN0 ~ 2^N - 1(如uint80~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. 跨类型比较intuint直接比较易出错 → 统一转换为相同类型再比较
进阶技巧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:普通地址,不可接收ETH
2. 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. 防范重入攻击
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//存钱罐合约
pragma solidity ^0.8.0;

contract PiggyBank {
    address public owner;

    constructor() {
        owner = msg.sender;
    }

    // 接收 ETH
    receive() external payable {}

    // 查询余额
    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }

    // 提取 ETH(只有 owner 可以)
    function withdraw() public {
        require(msg.sender == owner, "Only owner can withdraw");
        (bool success, ) = payable(owner).call{value: address(this).balance}("");
        require(success, "Withdraw failed");
    }
}

Solidity 合约类型总结

核心维度具体内容
合约类型定义Solidity 中合约本身是一种数据类型,与 uintaddress 等基础类型地位等同,可声明合约类型变量(如 Hello h)。
合约创建方式使用 new 关键字在合约中部署新合约(如 h = new Hello()),返回新合约实例。
合约交互方式通过合约类型变量调用目标合约的公开函数(如 h.sayHi()),实现合约间函数调用。
类型转换规则合约类型 ↔ 地址类型可双向转换:
1. 合约转地址:address(hello)
2. 地址转合约:Hello(helloAddr)
核心应用场景构建复杂合约系统(如工厂合约创建子合约、多合约协作交互)。
关键注意事项仅能调用目标合约的公开/外部函数;转换地址需确保对应合约已部署且类型匹配。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
pragma solidity ^0.8.0;

// 1. 定义 Counter 合约
contract Counter {
    // 计数状态变量
    uint public count;

    // 增加计数的函数
    function increment() public {
        count += 1;
    }

    // 获取当前计数的函数
    function getCount() public view returns (uint) {
        return count;
    }
}

// 2. 定义 CounterFactory 工厂合约
contract CounterFactory {
    // 存储创建的 Counter 合约实例
    Counter public counter;

    // 创建新的 Counter 合约
    function createCounter() public returns (address) {
        counter = new Counter();
        return address(counter);
    }

    // 调用 Counter 合约的 increment 函数
    function incrementCounter() public {
        // 检查合约是否已创建
        require(address(counter) != address(0), "Counter not created yet");
        counter.increment();
    }

    // 获取 Counter 合约的当前计数值
    function getCounterValue() public view returns (uint) {
        require(address(counter) != address(0), "Counter not created yet");
        return counter.getCount();
    }
}

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。
  1. Solidity 数组分固定/动态长度,核心区别是是否支持 push/pop 及长度能否调整;
  2. 数据位置决定数组操作能力(storage 功能最全,calldata 支持切片,memory 长度固定);
  3. string/bytes 作为特殊数组,功能有限且需关注 Gas 效率,大数组遍历需规避 Gas 超限风险。

你希望我根据这份 Solidity 中 string 与 bytes 的学习资料,生成一份结构化的总结表格,清晰梳理各类字节/字符串类型的核心信息。

Solidity 字节与字符串类型总结

类型分类具体类型核心特性主要使用场景Gas 效率核心操作/限制
定长字节数组bytes1 ~ bytes321. 值类型,赋值/传参拷贝
2. 长度固定不可变
3. 索引只读访问
4. 占用空间固定
存储哈希值、ID、固定长度原始字节数据⭐⭐⭐⭐⭐1. 支持比较/位运算/移位运算
2. 可通过 .length 获取长度
3. 索引仅可读,不可修改
动态字节数组bytes1. 引用类型(需指定 memory/storage/calldata)
2. 长度动态可变
3. 可修改元素
存储任意长度的原始字节数据⭐⭐⭐⭐1. 支持 push()/pop() 增删元素
2. 可通过索引修改值
3. bytes.concat() 拼接
4. 比 bytes1[] 更省 Gas
动态字节数组bytes1[]1. 动态数组,元素为 bytes1
2. 存储结构松散
几乎不推荐使用(仅兼容旧代码)⭐⭐操作同普通数组,但 Gas 消耗高
字符串类型string1. 引用类型,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
  1. 类型选择核心:固定长度数据选 bytes1~bytes32(优先 bytes32),变长原始字节选 bytes,文本数据选 string,避免使用 bytes1[]
  2. 操作核心string 的复杂操作(长度、索引、修改)需先转为 bytes,字符串比较需通过哈希值而非直接 ==
  3. 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仅当前合约可调用;子合约不可访问当前合约私有实现细节
选择原则优先最严格可见性(privateinternalexternalpublic),减少攻击面避免不必要的 public,辅助函数用 internal/private
状态可变性无修饰符可读写状态,不可接收 ETH普通状态修改(如赋值状态变量)
view仅读取状态,不可修改查询数据(如读取状态变量),外部调用免费
pure不读写状态,仅依赖入参计算纯数学运算(如加法、乘法)
payable可读写状态,可接收 ETH转账、存款等需要接收 ETH 的场景
选择原则不修改状态的函数必须标记 view/pure,编译器强制校验避免省略状态可变性修饰符,提升安全性和可读性
函数调用方式内部调用直接用函数名调用;同一执行上下文;msg.sender/msg.value 不变;gas 消耗低合约内部函数间调用,优先使用
外部调用通过 this/合约实例调用;新执行上下文;msg.sender 可能变化;gas 消耗高调用外部合约函数,需注意重入攻击风险
参数数据位置memory临时存储,函数调用后释放;可读写函数参数/局部变量(如字符串、数组)
calldata只读,外部函数参数专用;最省 gasexternal 函数的数组/字符串参数
storage永久存储,引用状态变量;gas 消耗高内部函数中引用状态变量(如结构体、数组)
函数重载规则同名函数,参数列表(个数/类型)不同;仅返回值不同无法重载实现同一功能的多参数版本(如两数相加/三数相加)
特殊函数constructor合约部署时执行一次;可标记 payable(部署时接收 ETH)初始化状态变量(如设置合约 owner、token 名称)
receive()接收纯 ETH 转账时触发;必须 external payable,无参数无返回值处理用户直接转账 ETH 的逻辑
fallback()调用不存在的函数/带数据的转账时触发;必须 external payable处理未知调用,兜底逻辑
多返回值语法支持多值返回;可命名返回值;支持解构赋值(忽略部分值用 ,一次性返回多个相关数据(如数字、布尔值、字符串)