火影 发表于 2025-4-1 17:58:12

【区块链安全 | 第二十篇】类型之运算符

https://i-blog.csdnimg.cn/direct/80a341128c454f628aa1595a78a12363.png
运算符

算术和位运算符即使在两个操作数类型不同的情况下也可以应用。例如,你可以计算 y = x + z,此中 x 是 uint8 类型,z 是 uint32 类型。在这种情况下,将使用以下机制来确定运算执行的类型(这对于溢出非常重要)以及运算符效果的类型:


[*]如果右操作数的类型可以隐式转换为左操作数的类型,则使用左操作数的类型。
[*]如果左操作数的类型可以隐式转换为右操作数的类型,则使用右操作数的类型。
[*]否则,不允许举行运算。
如果此中一个操作数是字面量数字,它起首会转换为其“移动类型”,即可以容纳该值的最小类型(雷同位宽的无符号类型被认为比有符号类型更小)。如果两个操作数都是字面量数字,则运算会使用现实所需的无穷精度举行计算,以确保在效果与非字面量类型一起使用时不会丢失精度。
运算符的效果类型与执行运算的类型雷同,比力运算符的效果类型总是 bool。
运算符 **(指数运算)、<< 和 >> 使用左操作数的类型举行运算和返回效果。
三元运算符

三元运算符用于形如 <表达式> ? <真表达式> : <假表达式> 的表达式。根据主表达式的效果,计算后两个表达式之一。如果 <表达式> 计算为真,则管帐算 <真表达式>,否则计算 <假表达式>。
三元运算符的效果类型不像其操作数那样是有理数类型,即使全部操作数都是有理数字面量。效果类型根据两个操作数的类型确定,起首如果需要则转换为它们的“移动类型”。
因此,像 255 + (true ? 1 : 0) 这样的表达式会由于算术溢出而回退。原因是 (true ? 1 : 0) 是 uint8 类型,这会导致加法也在 uint8 类型中举行,而 256 超出了该类型的范围。
另一个效果是像 1.5 + 1.5 这样的表达式是有效的,但 1.5 + (true ? 1.5 : 2.5) 是无效的。这是由于前者是一个有理数表达式,在无穷精度下计算,只有最终值很重要。而后者则涉及将一个有理数的分数转换为整数,这是当前不允许的。
复合运算符和增量/减量运算符

如果 a 是左值(即变量或可以被赋值的东西),以下运算符可作为简写形式使用:


[*]a += e 等价于 a = a + e。运算符 -=、*=、/=、%=、|=、&=、^=、<<= 和 >>= 按照雷同的规则定义。
[*]a++ 和 a-- 等价于 a += 1 / a -= 1,但表达式本身仍然使用 a 的旧值。相比之下,--a 和 ++a 对 a 的影响雷同,但会返回变更后的值。
删除运算符


[*]delete a 会将 a 的值重置为该类型的初始值。即,对于整数,它等价于 a = 0,但它也可以用于数组,这时会将数组的长度设为 0,大概将静态数组的全部元素设置为初始值。
[*]delete a 会删除数组中索引为 x 的项,并保持其他元素和数组的长度不变。这意味着数组会留下一个安定。如果你打算删除项,使用映射(mapping)可能是更好的选择。
对于结构体,delete a 会将结构体全部成员重置。换句话说,delete a 后的 a 值相当于 a 被声明但未赋值,要注意:delete 对映射没有影响(由于映射的键可能是任意的,一般是未知的)。因此,如果删除一个结构体,它会重置全部非映射的成员,而且会递归地重置成员,除非成员是映射类型。不外,映射中的单个键及其值可以被删除:如果 a 是一个映射,delete a 会删除 x 索引处存储的值。
需要注意的是,delete a 现实上像是对 a 举行赋值操作,即它会把一个新对象存储到 a 中。这种区别在 a 是引用变量时尤为显着:它只会重置 a 本身,而不会影响它之前引用的值。
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;

contract DeleteExample {
    uint data;// 定义一个 uint 类型的变量 data
    uint[] dataArray;// 定义一个 uint 数组 dataArray

    function f() public {
      uint x = data;// 将 data 的值赋给 x
      delete x; // 将 x 设置为 0,删除 x 对变量 data 的影响。x 是一个局部变量,不会影响 data
      delete data; // 将 data 设置为 0,删除操作仅影响 data,不会影响 x(因为 x 是局部变量)

      uint[] storage y = dataArray; // 声明一个引用变量 y 指向 dataArray 的存储
      delete dataArray; // 删除 dataArray,结果是 dataArray 的长度被设置为 0,并且由于 y 是引用类型,y 也会被影响
      // 对于引用类型的变量,delete 会影响存储对象本身,删除数组的所有元素
      // 但注意,"delete y" 是无效的,因为不能对引用类型的局部变量执行删除操作
      assert(y.length == 0); // 断言 dataArray 的长度已变为 0,验证删除操作生效
    }
}
运算符的优先级次序

以下是运算符的优先级次序,按求值次序列出。
优先级描述运算符1后缀增量和减量++, --新表达式new <typename>数组下标<array>[<index>]成员访问<object>.<member>雷同函数的调用<func>(<args...>)括号(<statement>)2前缀增量和减量++, --一元负号-一元运算符delete逻辑非!按位非~3指数运算**4乘法、除法和取余*, /, %5加法和减法+, -6按位移位运算符<<, >>7按位与运算符&8按位异或运算符^9按位或运算符`10不等式运算符<, >, <=, >=11等式运算符==, !=12逻辑与运算符&&13逻辑或运算符`14三元运算符<conditional> ? <if-true> : <if-false>赋值运算符=, `15逗号运算符,
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【区块链安全 | 第二十篇】类型之运算符