<<WTF-Solidity>>学习笔记(part 5-8)

news/2024/11/29 18:10:51/

part 5 :

数据位置

Solidity数据存储位置有三类:storagememorycalldata。不同存储位置的gas成本不同。storage类型的数据存在链上,类似计算机的硬盘,消耗gas多;memorycalldata类型的临时存在内存里,消耗gas少。整体消耗gas从多到少依次为:storage > memory > calldata。大致用法:

  1. storage:合约里的状态变量默认都是storage,存储在链上。

  2. memory:函数里的参数和临时变量一般用memory,存储在内存中,不上链。尤其是如果返回数据类型是变长的情况下,必须加memory修饰,例如:string, bytes, array和自定义结构。

  3. calldata:和memory类似,存储在内存中,不上链。与memory的不同点在于calldata变量不能修改(immutable),一般用于函数的参数

数据位置和赋值规则

在不同存储类型相互赋值时候,有时会产生独立的副本(修改新变量不会影响原变量),有时会产生引用(修改新变量会影响原变量)。规则如下:

  • 赋值本质上是创建引用指向本体,因此修改本体或者是引用,变化可以被同步:

    • storage(合约的状态变量)赋值给本地storage(函数里的)时候,会创建引用,改变新变量会影响原变量。例子:

      uint[] x = [1,2,3]; // 状态变量:数组 xfunction fStorage() public{//声明一个storage的变量 xStorage,指向x。修改xStorage也会影响xuint[] storage xStorage = x;xStorage[0] = 100;
      }

变量的作用域

Solidity中变量按作用域划分有三种,分别是状态变量(state variable),局部变量(local variable)和全局变量(global variable)

1. 状态变量

状态变量是数据存储在链上的变量,所有合约内函数都可以访问,gas消耗高。状态变量在合约内、函数外声明:

contract Variables {uint public x = 1;uint public y;string public z;
}

我们可以在函数里更改状态变量的值:

function foo() external{// 可以在函数里更改状态变量的值x = 5;y = 2;z = "0xAA";
}

2. 局部变量

局部变量是仅在函数执行过程中有效的变量,函数退出后,变量无效。局部变量的数据存储在内存里,不上链,gas低。局部变量在函数内声明:

function bar() external pure returns(uint){uint xx = 1;uint yy = 3;uint zz = xx + yy;return(zz);
}

3. 全局变量

全局变量是全局范围工作的变量,都是solidity预留关键字。他们可以在函数内不声明直接使用:

function global() external view returns(address, uint, bytes memory){address sender = msg.sender;uint blockNum = block.number;bytes memory data = msg.data;return(sender, blockNum, data);
}

在上面例子里,我们使用了3个常用的全局变量:msg.senderblock.numbermsg.data,他们分别代表请求发起地址,当前区块高度,和请求数据。下面是一些常用的全局变量,更完整的列表请看这个链接:

  • blockhash(uint blockNumber): (bytes32) 给定区块的哈希值 – 只适用于最近的256个区块, 不包含当前区块。
  • block.coinbase: (address payable) 当前区块矿工的地址
  • block.gaslimit: (uint) 当前区块的gaslimit
  • block.number: (uint) 当前区块的number
  • block.timestamp: (uint) 当前区块的时间戳,为unix纪元以来的秒
  • gasleft(): (uint256) 剩余 gas
  • msg.data: (bytes calldata) 完整call data
  • msg.sender: (address payable) 消息发送者 (当前 caller)
  • msg.sig: (bytes4) calldata的前四个字节 (function identifier)
  • msg.value: (uint) 当前交易发送的 wei 值
  • block.blobbasefee: (uint) 当前区块的blob基础费用。这是Cancun升级新增的全局变量。
  • blobhash(uint index): (bytes32) 返回跟当前交易关联的第 index 个blob的版本化哈希(第一个字节为版本号,当前为0x01,后面接KZG承诺的SHA256哈希的最后31个字节)。若当前交易不包含blob,则返回空字节。这是Cancun升级新增的全局变量

part 7 :

映射Mapping

在映射中,人们可以通过键(Key)来查询对应的值(Value),比如:通过一个人的id来查询他的钱包地址。

声明映射的格式为mapping(_KeyType => _ValueType),其中_KeyType_ValueType分别是KeyValue的变量类型。例子:

mapping(uint => address) public idToAddress; // id映射到地址
mapping(address => address) public swapPair; // 币对的映射,地址到地址

映射的规则

  • 规则1:映射的_KeyType只能选择Solidity内置的值类型,比如uintaddress等,不能用自定义的结构体。而_ValueType可以使用自定义的类型。下面这个例子会报错,因为_KeyType使用了我们自定义的结构体:

    // 我们定义一个结构体 Struct
    struct Student{uint256 id;uint256 score; 
    }
    mapping(Student => uint) public testVar;
  • 规则2:映射的存储位置必须是storage,因此可以用于合约的状态变量,函数中的storage变量和library函数的参数(见例子)。不能用于public函数的参数或返回结果中,因为mapping记录的是一种关系 (key - value pair)。

  • 规则3:如果映射声明为public,那么Solidity会自动给你创建一个getter函数,可以通过Key来查询对应的Value

  • 规则4:给映射新增的键值对的语法为_Var[_Key] = _Value,其中_Var是映射变量名,_Key_Value对应新增的键值对。例子:

    function writeMap (uint _Key, address _Value) public{idToAddress[_Key] = _Value;
    }

映射的原理

  • 原理1: 映射不储存任何键(Key)的资讯,也没有length的资讯。

  • 原理2: 对于映射使用keccak256(h(key) . slot)计算存取value的位置。感兴趣的可以去阅读 WTF Solidity 内部规则: 映射存储布局

  • 原理3: 因为Ethereum会定义所有未使用的空间为0,所以未赋值(Value)的键(Key)初始值都是各个type的默认值,如uint的默认值是0。

part 8 :

变量初始值

Solidity中,声明但没赋值的变量都有它的初始值或默认值。这一讲,我们将介绍常用变量的初始值。

值类型初始值

  • booleanfalse
  • string""
  • int0
  • uint0
  • enum: 枚举中的第一个元素
  • address0x0000000000000000000000000000000000000000 (或 address(0))
  • function
    • internal: 空白函数
    • external: 空白函数


http://www.ppmy.cn/news/1550957.html

相关文章

SQL:多字段混合去重后编号

SQL SERVER 库表存储人员记录,来自多种源数据,如果两条记录的 Name、Phone、Email 字段之一有重复,则说明这两条记录是同一个人。null 是数据未知,两条记录的字段都为 null 时表示两者默认不重复,是否重复要靠其他字段…

React Router v7正式发布:开启全栈框架新时代

概述 2024年11月22日,React Router团队宣布React Router v7正式发布。这一重大版本不仅将Remix的大量特性整合到了React Router中,还为React Router用户开启了全新的“框架模式”,使得开发者可以直接使用Remix的功能。这标志着React Router从…

设计有一个 “分布式软总线“ 系统,跨平台

设计一个 跨平台的分布式软总线 系统是为了实现不同设备间的通信,支持各种硬件平台和操作系统,且能够通过统一的协议进行互联互通。这样的系统通常用于物联网(IoT)场景、智能家居、智能制造、车联网等应用。以下是一个详细的设计方…

Spring Cloud Stream实现数据流处理

1.什么是Spring Cloud Stream? Spring Cloud Stream的核心是Stream,准确来讲Spring Cloud Stream提供了一整套数据流走向(流向)的API, 它的最终目的是使我们不关心数据的流入和写出,而只关心对数据的业务处…

Linux快速入门:3.硬盘分区与RHEL8系统安装

点击蓝字 | 关注我们 规划Linux的硬盘分区 在安装RHEL8前,我们先根据上节的内容Linux快速入门:2.Linux的文件系统和目录结构,对硬盘进行分区规划。 对于Linux初学者,在学习过程中建议分区方案越简单越好,只需要将硬盘分…

Vim操作指南

Vim 是一款功能强大的文本编辑器,它广泛应用于程序员、系统管理员和普通用户的日常工作中。Vim 以其高效、灵活和可扩展性著称,虽然上手有一定难度,但掌握后可以极大提高编辑效率。本指南旨在为用户提供一个详细的 Vim 操作教程,从…

人工智能技术的工程伦理问题 --以美团外卖大数据杀熟为例

引言 最近朋友圈刷屏的一篇文章《我被美团会员割了韭菜》很多人都看过,针对该事件,网经社电子商务研究中心主任曹磊指出:大数据“杀熟”暴露出大数据产业发展过程中的非对称以及不透明。由此也带来的最大伦理危机—个人隐私权问题。 背景信…

深度学习:完整的模型训练流程

深度学习:完整的模型训练流程 为了确保我们提供一个彻底和清晰的指导,让我们深入分析在model.py和train.py文件中定义的模型训练和验证流程。以下部分将详细讨论模型结构的定义、数据的加载与预处理、训练参数的配置、训练与测试循环,以及模…