Solidity 值类型详解

Solidity 值类型详解

学习目标

  • 掌握 Solidity 的整型、布尔、地址、定长字节数组、枚举等值类型
  • 理解 EVM 256 位机器架构对类型设计的影响
  • 掌握类型转换规则(隐式 vs 显式)
  • 了解溢出问题的历史与解决方案
  • 理解 Solidity 中所有类型的默认值机制

一、整型(int / uint)

1.1 基本概念

Solidity 提供有符号整型 int 和无符号整型 uint,从 8 位到 256 位,以 8 位为步长

无符号 有符号 位宽 范围
uint8 int8 8 位 0~255 / -128~127
uint16 int16 16 位 0~65535 / -32768~32767
uint32 int32 32 位 0~4,294,967,295
... ... ... ...
uint256 int256 256 位 0~2^256-1
  • uintuint256 的别名
  • intint256 的别名

1.2 为什么是 256 位?

EVM(以太坊虚拟机)是一台 256 位的栈式虚拟机,其内部寄存器和栈槽位宽度均为 256 位。因此 uint256 / int256 是 EVM 的原生操作类型,使用其他位宽的类型在某些情况下反而可能消耗更多 Gas(因为需要额外的掩码操作)。

1.3 溢出问题

整型溢出是智能合约安全的重大隐患:

低版本(< 0.8.0):取模行为

uint8 x = 255;
x = x + 1;  // 结果为 0,发生溢出但不报错!

这种静默溢出导致了大量安全事故(如 2018 年的 BEC Token 漏洞)。

高版本(>= 0.8.0):自动检查,溢出抛异常

uint8 x = 255;
x = x + 1;  // 交易 revert,自动检测到溢出

SafeMath 库(历史方案)

在 0.8.0 之前,开发者需要使用 OpenZeppelin 的 SafeMath 库来手动防止溢出。0.8.0 之后编译器内置了溢出检查,SafeMath 不再需要。

如果你确实需要溢出取模行为(极少数场景),可以使用 unchecked 块:
solidity
unchecked { x = x + 1; }

1.4 获取类型边界

使用 type(T).mintype(T).max 获取类型的最小值和最大值:

type(uint8).min   // 0
type(uint8).max   // 255
type(int8).min    // -128
type(int8).max    // 127
type(uint256).max // 2^256 - 1(一个极大的数)

1.5 类型转换

隐式转换(窄 → 宽):安全,自动进行

uint8 x = 8;
uint16 y = x;  // OK:uint8 → uint16,范围变宽,不丢失数据

显式转换(强制):可能丢失数据,需手动声明

uint16 y = 256;
uint8 x = uint8(y);  // 强制截断:x = 0(256 超出 uint8 范围)

1.6 完整示例

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;

contract BasicType {
    function testInt() public pure returns (uint) {
        uint256 max = type(uint256).max;
        uint8 x = 8;
        uint16 y = 9;
        y = x;          // 隐式转换:y比x表达范围宽
        x = uint8(y);   // 显式强制类型转换
        return max;
    }

    // 枚举类型
    enum OrderState {
        layorder,
        payment
    }
    function enumTest() public pure returns (OrderState) {
        OrderState state = OrderState.payment;
        return state;
    }
}

二、布尔类型(bool)

布尔类型只有两个值:truefalse

bool isActive = true;
bool isExpired = false;

虽然逻辑上只需要 1 位,但在 EVM 中布尔值实际占用 8 位(1 字节) 的存储空间。支持的运算符:

运算符 说明
! 逻辑非
&& 逻辑与(短路求值)
\|\| 逻辑或(短路求值)
== 等于
!= 不等于

注意:Solidity 不支持将布尔值与整型互相转换(不像 C 语言中 0 为 false、非 0 为 true)。


三、地址类型(address)

3.1 基本概念

地址类型是 Solidity 中最重要的类型之一,长度为 20 字节(160 位)

address addr = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;

3.2 address 与 address payable

Solidity 区分两种地址类型:

类型 说明 特有方法
address 普通地址 balance, code, codehash, call, delegatecall, staticcall
address payable 可支付地址 address 基础上增加 transfersend
address payable recipient = payable(0x123...);
recipient.transfer(1 ether);  // 发送 ETH,失败自动 revert
bool success = recipient.send(1 ether);  // 发送 ETH,失败返回 false

3.3 类型转换

地址类型可以与 uint160bytes20 互相转换:

address addr = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
uint160 num = uint160(addr);    // address → uint160
bytes20 data = bytes20(addr);   // address → bytes20
address back = address(num);    // uint160 → address

3.4 合约地址 vs EOA 地址

以太坊有两种账户类型,它们的地址格式完全相同:

类型 说明 判断方式
EOA(外部账户) 由私钥控制,可以发起交易 addr.code.length == 0
合约账户 由代码控制,被交易触发执行 addr.code.length > 0

3.5 常用成员

addr.balance           // 获取地址的 ETH 余额(单位 wei)
addr.code              // 获取地址的合约字节码
addr.call(data)        // 底层调用,返回 (bool, bytes)
addr.delegatecall(data) // 委托调用
addr.staticcall(data)   // 静态调用(只读)

3.6 示例

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;

contract ComplexValueType {
    function testAddress() public view returns (address) {
        address addr = msg.sender; // 函数调用者的账号地址
        return addr;
    }

    function testMyAddress() public view returns (address) {
        address addr = address(this); // 当前合约地址
        return addr;
    }

    // 合约类型
    function testContract() public view {
        ComplexValueType mycontract = this;
    }

    // 定长字节数组:只读不可写
    function testFixedByteArray() public pure returns (bytes3) {
        bytes3 data = 0x111111;
        bytes1 first = data[0]; // 下标访问
        return first;
    }
}

四、合约类型(Contract Type)

4.1 基本概念

每个 contract 定义都会生成一个独立的数据类型。合约类型的变量本质上持有该合约的地址引用。

contract MyToken { ... }

// 在其他合约中
MyToken token = MyToken(0x123...);  // 用地址实例化合约引用
token.transfer(to, amount);         // 调用合约函数

4.2 核心特性

  • 隐式转换为父合约(多态):如果 Child 继承 Parent,则 Child 类型可隐式转为 Parent 类型
  • 显式转换为 addressaddress(myContract) 获取合约地址
  • 不支持运算符:合约类型不能做加减比较等运算
  • new 部署新合约
contract Factory {
    function createToken() public returns (MyToken) {
        MyToken token = new MyToken();  // 部署新合约并返回引用
        return token;
    }
}

五、定长字节数组(bytes1 ~ bytes32)

5.1 基本概念

Solidity 提供从 bytes1bytes32 共 32 种定长字节数组类型。

重要区分:定长字节数组(如 bytes32)是值类型,而动态字节数组(bytes)和一般数组是引用类型

5.2 特性

特性 说明
下标访问 data[0] 获取第一个字节,返回 bytes1
length 属性 返回字节数(编译时确定,不可变)
只读不可写 data[0] = 0xff 是非法的

5.3 应用场景

应将定长字节数组视为一个整体来使用,而不是当作可变数组操作:

bytes32 hash = keccak256(abi.encodePacked("hello"));  // 哈希值
bytes4 selector = bytes4(keccak256("transfer(address,uint256)"));  // 函数选择器
bytes20 addrBytes = bytes20(msg.sender);  // 地址的字节表示

六、枚举类型(enum)

6.1 基本概念

枚举是 Solidity 中的用户自定义类型之一(另外两个是 contractstruct)。它用于定义一组有限的命名常量。

6.2 规则

  • 最少 1 个成员,最多 256 个成员(因为底层用 uint8 存储)
  • 默认值为第一个成员(序号 0)
  • 与整型的转换必须用显式转换
enum Color { Red, Green, Blue }
uint8 idx = uint8(Color.Green);  // 1
Color c = Color(0);              // Color.Red

6.3 完整示例

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

contract Enum {
    enum Status {
        None, Pending, Shipped, Completed, Rejected, Canceled
    }
    Status public status;

    struct Order {
        address buyer;
        Status status;
    }
    Order public orders;

    function get() external view returns (Status) {
        return status;
    }

    function set(Status _status) external {
        status = _status;
    }

    function ship() external {
        status = Status.Shipped;
    }

    function reset() external {
        delete status; // 重置为默认值(第一个定义的值)
    }
}

七、默认值

Solidity 中没有 undefinednull 的概念。所有变量在声明时如果未赋值,都会自动初始化为该类型的默认值:

类型 默认值
bool false
uint / int 0
address 0x0000000000000000000000000000000000000000(零地址)
bytes1 ~ bytes32 全零字节
enum 第一个成员(序号 0)
string 空字符串 ""
bytes(动态) 空字节数组

使用 delete 关键字可以将变量重置为默认值:
solidity
uint x = 42;
delete x; // x 变回 0


八、综合示例

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

contract ValueTypes {
    bool b = true;
    uint public u = 123;
    int public i = -123;

    int public minInt = type(int).min;
    int public maxInt = type(int).max;
    address public addr = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
    bytes32 public b32 = keccak256(abi.encodePacked("This is a test value"));
}

部署此合约后,你可以通过 Remix 直接点击各个 public 变量名来查看它们的值:

  • u → 123
  • i → -123
  • minInt → -57896044618658097711785492504343953926634992332820282019728792003956564819968
  • maxInt → 57896044618658097711785492504343953926634992332820282019728792003956564819967
  • addr → 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
  • b32 → 一个 32 字节的 keccak256 哈希值

小结

本节我们详细学习了 Solidity 的值类型系统。值类型的核心特征是:赋值和传参时会完整拷贝一份数据(而非引用)。掌握这些基础类型是编写安全、高效智能合约的前提。下一节我们将学习引用类型(数组、结构体、映射)以及数据存储位置(storage / memory / calldata)。


本文为 Web3 区块链系列第 4 篇,共 12 篇。

主题测试文章,只做测试使用。发布者:Walker,转转请注明出处:https://walker-learn.xyz/archives/7488

(0)
Walker的头像Walker
上一篇 5天前
下一篇 2025年11月25日 02:00

相关推荐

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

    引用类型详解:数组、结构体、映射、字符串 学习目标 掌握 Solidity 中四种引用类型(数组、结构体、映射、字符串/变长字节数组)的定义和使用方法。 前置知识 已学习值类型(整型、布尔、地址、定长字节数组等)。 数组(Array) storage 中的数组 Solidity 中数组分为两种: 静态数组 T[K]:长度固定,编译时确定 动态数组 T[]:长…

  • 函数定义与访问控制

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

    Web3与WASM 23小时前
    000
  • 动态调用与Fallback机制

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

    21小时前
    000
  • 区块链核心技术

    区块链核心技术 学习目标 理解区块链底层核心技术:哈希函数、默克尔树、数字签名、加密技术 掌握主流共识机制的原理与分类 了解区块的运行原理与整体架构 认识联盟链的特点与应用场景 前置知识 基本的计算机科学概念(数据结构、算法) 对密码学有初步认知(非必须) 已阅读 01-Web3 概述与愿景 一、哈希函数 1.1 什么是哈希函数 哈希函数(Hash Func…

    1天前
    200
  • 存储位置与拷贝机制:storage、memory、calldata

    存储位置与拷贝机制:storage、memory、calldata 学习目标 理解 EVM 中三种数据存储位置的特点,以及引用类型在不同存储位置之间赋值时的拷贝规则。 前置知识 已学习值类型和引用类型(数组、结构体、映射、字符串)。 三种存储位置 storage —— 持久化存储 类似数据库,数据永久保存在区块链上 成员变量(状态变量)默认存储在 stora…

简体中文 繁体中文 English