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 22小時前
    000
  • 動態調用與Fallback機制

    動態調用與Fallback機制 學習目標 掌握 call 動態調用的語法與使用場景 理解 calldata 數據結構(selector + 參數編碼) 掌握 fallback / receive 函數的觸發機制 理解 tx、msg、block 三種上下文變量的區別 call 動態調用 基本語法 (bool success, bytes memory data…

    20小時前
    000
  • 區塊鏈核心技術

    區塊鏈核心技術 學習目標 理解區塊鏈底層核心技術:哈希函數、默克爾樹、數字簽名、加密技術 掌握主流共識機制的原理與分類 瞭解區塊的運行原理與整體架構 認識聯盟鏈的特點與應用場景 前置知識 基本的計算機科學概念(數據結構、算法) 對密碼學有初步認知(非必須) 已閱讀 01-Web3 概述與願景 一、哈希函數 1.1 甚麼是哈希函數 哈希函數(Hash Func…

    1天前
    100
  • 存儲位置與拷貝機制:storage、memory、calldata

    存儲位置與拷貝機制:storage、memory、calldata 學習目標 理解 EVM 中三種數據存儲位置的特點,以及引用類型在不同存儲位置之間賦值時的拷貝規則。 前置知識 已學習值類型和引用類型(數組、結構體、映射、字符串)。 三種存儲位置 storage —— 持久化存儲 類似數據庫,數據永久保存在區塊鏈上 成員變量(狀態變量)默認存儲在 stora…

    Web3與WASM 23小時前
    200
簡體中文 繁體中文 English