solidity0.8.0中如何使用内联汇编

本文介绍了如何在Solidity 0.8.0中使用内联汇编,包括其作用、基本语法、变量定义与赋值、循环和判断语句的实现。内联汇编允许开发者实现节约gas、处理复杂操作,但需要注意不同汇编块间不共享变量。文章通过实例解析了如何进行简单的汇编操作、函数定义以及如何访问和操作EVM中的变量。
摘要由CSDN通过智能技术生成

内联汇编的作用:

1)节约gas

2)实现solidity不能或者不好实现的功能。

3)对EVM有更细粒度的控制,在编写库函数时很有用。

基本汇编语法

内联汇编就是对数据的入栈和出栈进行处理。

内联汇编中使用的所有变量都是值类型

内联汇编中,所有的类型都是uint256

solidity中,内存数据的元素总是占用32bytes倍数的大小空间

先是存储数组的元素个数,然后再是实际元素。

1、引入汇编

在Solidity中使用assembly{}来引入汇编代码段,这被称为内联汇编:

assembly {
 // some assembly code here
}
在assembly块内的代码开发语言被称为Yul,为了简化我们称其为 汇编、汇编代码或EVM汇编。

 另一个需要注意的问题是,汇编代码块之间不能通信,也就是说在 一个汇编代码块里定义的变量,在另一个汇编代码块中不可以访问。 例如:

Solidity 文档有这样的解释:
不同的内联汇编块不共享任何名称空间,即不可能调用Yul函数或访问在其他内联汇编块中定义的Yul变量。

assembly { 
    let x := 2
}
        
assembly {
    let y := x          // Error
}
// DeclarationError: identifier not found
// let y := x
// ^

 第二个汇编代码使用了第一个汇编代码中的变量x,就会报错。

2、简单汇编示例和运行原理

下面的代码使用内联汇编代码计算函数的两个参数的和并返回结果:

以下是一个简单的示例,函数接受两个参数,并将它们的和作为返回值,看看使用 Assembly是怎么实现的?了解它们在 EVM的工作方式。

// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.0;



contract AssemblyExample {



   function addition(uint x, uint y) public pure returns (uint) {



    assembly {



        //声明一个 result 变量,并将 x,y之和赋值给它

        let result := add(x, y)   // x + y



        //使用 mstore 操作码将 result存在 memory 中,地址是 0x0

        mstore(0x0, result)       // store result in memory



        //返回 32 字节的 memory 地址

        return(0x0, 32)          



    }

}



}

让我们来看看一个简单的例子。我们将数据存放在storage(存储)中,然后再去调用它。

// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.0;



contract StorageDataExample {



    function setData(uint256 newValue) public {

        assembly {

            sstore(0, newValue)

        }

    }



    function getData() public view returns(uint256) {

        assembly {

            let v := sload(0)

            mstore(0x80, v)

            return(0x80, 32)

        }

    }

}

img

 

setData函数使用了sstore操作码将变量newValue写入storage(存储)中。

getData函数先是用了sload操作码来加载storage(存储)中的数据,它并不能从storage中直接返回。所以才需要mstore操作码将其写入memory(内存)中,最后我们返回引用memory(内存)中存放数据的地址和 32 字节长度的数据。

3、汇编中的变量定义与赋值 

在Yul中,使用let关键字定义变量。使用:=操作符给变量赋值:

assembly {
 let x := 2
}

 和Solidity 不同,Solidity只需要用=, 因此不要忘了 :.

 如果没有使用:=操作符给变量赋值,那么该变量自动初始化为0值:

assembly {
 let x           // 自动初始化为 x = 0
 x := 5          // x 现在的值是5
}

你可以使用复杂的表达式为变量赋值,例如:

assembly {
 let x := 7 
 let y := add(x, 3)
 let z := add(keccak256(0x0, 0x20), div(slength, 32)) 
 let n  // 自动初始化为 n = 0
}

 汇编中let指令的运行机制

在EVM的内部,let指令执行如下任务:

  • 创建一个新的堆栈槽位
  • 为变量保留该槽位
  • 当到达代码块结束时自动销毁该槽位

因此,使用let指令在汇编代码块中定义的变量,在该代码块 外部是无法访问的。

4、汇编的注释

在Yul汇编中注释的写法和Solidity一样,可以使用单行注释// 或多行注释/* */。例如:

assembly { 
 // 单行注释

 /*
  多
  行
  注释
 */
}

5、汇编中的字面量

在Solidity汇编中字面量的写法与Solidity一致。不过,字符串字面量最多可以包含32个字符。

assembly { 
 let a := 0x123             // 16进制
 let b := 42                // 10进制
 let c := "hello world"     // 字符串

 let d := "very long string more than 32 bytes" // 超长字符串,出错!
}

// TypeError: String literal too long (35 < 32)
// let d := "really long string more than 32 bytes"
// 

 6、汇编中的块和作用域

在下面的示例中,y和z仅在定义所在块范围内有效。因此y变量的作用范围是scope 1,z变量的作用范围是scope 2

assembly { 
 let x := 3          // x在各处可见
  
 // Scope 1 
 { 
 let y := x     // ok 
 }  // 到此处会销毁y

 // Scope 2 
 { 
 let z := y     // Error 
 } // 到此处会销毁z
}

// DeclarationError: 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值