---
title: 'Solidity WTF 101 7 單元'
lang: zh-tw
---
Solidity WTF 101 7 單元
===
:::info
:date: 2024/09/25
:::
[TOC]
## 補充
### uint問題
其他學員有提到
```
請問uint8 uint256的8位 256位數
是指十進制整數的8位,256位嗎?
還是2進制的8位跟256位呢?
```
在提到之前也有這個疑問 但答案就是2進制。
* uint8 表示一個 8位元的無符號整數,意思是它可以表示的數值範圍是二進制 8 個位元 (bits)。它可以表示的範圍是從 0 到 2^8 - 1(即 0 到 255)。
* uint256 表示一個 256位元的無符號整數,它可以表示的範圍是從 0 到 2^256 - 1,這是一個非常大的範圍。
## 課程學習
### 映射關係
下面第一行代表一個型別為`uint`的數字,對應到一個`address`,白話就是如果我輸入`1`,那他就會返回`1`的`address`,因為他是映射關係,可以當成`1`是`key`,`address`是`value`,只要給予`key`就會返回`value`這就是映射,不只在`Solidity`很常使用,在其他語言上也會很常使用到`mapping`的概念。
```xml=
mapping(uint => address) public idToAddress; // id映射到地址
mapping(address => address) public swapPair; // 币对的映射,地址到地址
```
### 映射規則
:::info
:information_source: 以下是從課程中直接複製過來,我認為很淺顯易懂,所以直接copy過來
:::
* 規則1
只能使用`Solidity`內規定的預設型別,不能使用自訂的`Struct`
```xml=
<!-- 這是錯誤範例 -->
struct Student{
uint256 id;
uint256 score;
}
<!-- 無法使用自訂義結構當作key -->
mapping(Student => uint) public testVar;
```
* 规则2:映射的存储位置必须是storage,因此可以用于合约的状态变量,函数中的storage变量和library函数的参数(见例子)。不能用于public函数的参数或返回结果中,因为mapping记录的是一种关系 (key - value pair)。
* 规则3:如果映射声明为public,那么Solidity会自动给你创建一个getter函数,可以通过Key来查询对应的Value。
* 规则4:给映射新增的键值对的语法如下
```xml=
function writeMap (uint _Key, address _Value) public{
idToAddress[_Key] = _Value;
}
```
### 映射原理
:::info
:information_source: 以下是從課程中直接複製過來,我認為很淺顯易懂,所以直接copy過來
:::
* `原理1`: 映射不储存任何键(Key)的资讯,也没有`length`的资讯。
* `原理2`: 映射使用`keccak256(abi.encodePacked(key, slot))`当成`offset`存取`value`,其中slot是映射变量定义所在的插槽位置。
* `原理3`: 因为`Ethereum`会定义所有未使用的空间为0,所以未赋值(Value)的键(Key)初始值都是各个`type`的默认值,如`uint`的默认值是`0`。
### 儲存機制
---
| 類型 | 永久存储(Storage) | 記憶體(Memory) |
| -------- | -------- | -------- |
| 存儲位置 | 區塊鏈上,永久保存 | 臨時存儲,只在函數調用期間存在 |
| 變量類型 | 狀態變量(如 `uint`, `mapping`, `struct` 等) | 局部變量和函數參數 |
| 存取成本 | 高`Gas Fee` | 低`Gas Fee` |
| 數據持久性 | 持久化,交易結束後仍然存在 | 函數執行完畢後數據被釋放 |
| 映射存儲位置 | 使用 `keccak256` 計算哈希值,將 `key` 和 `slot` 結合,存儲在鏈上 | 映射的值不會存儲在記憶體中 |
### 映射的存儲過程
---
當你定義一個映射時,例如 `mapping(uint => uint) myMap;`,系統會將 `myMap` 分配一個存儲插槽(slot)。但實際的 `key-value` 並不是直接存儲在這個插槽中,而是通過以下方式計算出存儲地址:
1. 計算存儲位置:
* `slot` 是映射變量的插槽位置,例如 `myMap` 在 `slot` = 1。
* `key` 是映射中存儲的具體鍵值。
* 存儲位置的計算公式為:`keccak256(abi.encodePacked(key, slot))`。
2. 存儲過程:
* `Solidity` 使用 `keccak256` 將 `key` 和 `slot` 進行哈希計算,這個哈希結果用來作為映射中具體 `key-value` 對的存儲位置。
* 這些數據被存儲在區塊鏈的永久存儲區中。
### 簡單流程圖描述:
---
```xml=
myMap (slot 1) key = 123
| |
slot 1 + key -> keccak256(123 + 1) -> 假設結果為位置 0xabc
|
存儲 myMap[123] 對應的 value
```
### 例子:
---
假設我們有一個智能合約:
```xml=
contract MyContract {
mapping(uint => uint) public myMap; // slot 1
}
```
1. 映射變量 `myMap` 的插槽是 1。
2. 當你訪問 `myMap[123]` 時,會計算哈希 `keccak256(abi.encodePacked(123, 1))`,得到一個存儲地址,例如 `0xabc`。
3. `myMap[123]` 的值將存儲在這個位置 `0xabc`,並且這個值會永久存儲在鏈上,直到被更改。
### 印射例子:
```javascript=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleMappingExample {
// 定義一個從地址到 uint 的映射
mapping(address => uint) public addressToAmount;
// 設置某個地址對應的數值
function setAmount(uint _amount) public {
// 詳細解釋就是addressToAmount這個map,他對應的key(address msg.sende 也就是操作此合約的address),value是_amount
addressToAmount[msg.sender] = _amount;
}
// 查詢某個地址對應的數值
function getAmount(address _address) public view returns (uint) {
// 你直接調用key會帶出value,與array[0]概念差不多
return addressToAmount[_address];
}
}
```