solidity语言基础2
发布日期:2021-06-28 22:10:28
浏览次数:2
分类:技术文章
本文共 6184 字,大约阅读时间需要 20 分钟。
1.杂项 映射/字典(mappings) 映射或字典类型,一种键值对的映射关系存储结构。定义方式为mapping(_KeyType => _KeyValue)。键的类型允许除映射外的所有类型,如数组,合约,枚举,结构体。值的类型无限制。 映射可以被视作为一个哈希表,其中所有可能的键已被虚拟化的创建,被映射到一个默认值(二进制表示的零)。但在映射表中, 我们并不存储键的数据,仅仅存储它的keccak256哈希值,用来查找值时使用。 因此,映射并没有长度,键集合(或列表),值集合(或列表)这样的概念。 映射类型,仅能用来定义状态变量,或者是在内部函数中作为storage类型的引用。引用是指你可以声明一个,如var storage mappVal的用于存储状态变量的引用的对象,但你没办法使用非状态变量来初始化这个引用。 可以通过将映射标记为public,来让Solidity创建一个访问器。要想访问这样的映射,需要提供一个键值做为参数。如果映射的值类型也是映射,使用访问器访问时,要提供这个映射值所对应的键,不断重复这个过程。下面来看一个例子: contract MappingExample{ mapping(address => uint) public balances; function update(uint amount) returns (address addr){ balances[msg.sender] = amount; return msg.sender; } } 由于调试时,你不一定方便知道自己的发起地址,所以把这个函数,略微调整了一下,以在调用时,返回调用者的地址。编译上述合同后,可以先调用update(),执行成功后,查看调用信息,能看到你更新的地址,这样再查一下这个地址的在映射里存的值。 如果你想通过合约进行上述调用。 pragma solidity ^0.4.0; //file indeed for compile //may store in somewhere and import contract MappingExample{ mapping(address => uint) public balances; function update(uint amount) returns (address addr){ balances[msg.sender] = amount; return msg.sender; } } contract MappingUser{ address conAddr; address userAddr; function f() returns (uint amount){ //address not resolved! //tringing conAddr = hex"0xf2bd5de8b57ebfc45dcee97524a7a08fccc80aef"; userAddr = hex"0xca35b7d915458ef540ade6068dfe2f44e8fa733c"; return MappingExample(conAddr).balances(userAddr); } } 映射并未提供迭代输出的方法,可以自行实现一个数据结构 左值的相关运算符 左值,是指位于表达式左边的变量,可以是与操作符直接结合的形成的,如自增,自减;也可以是赋值,位运算。 可以支持操作符有:-=,+=,*=,%=,|=,&=,^=,++,--。 特殊的运算符delete delete运算符,用于将某个变量重置为初始值。对于整数,运算符的效果等同于a = 0。而对于定长数组,则是把数组中的每个元素置为初始值,变长数组则是将长度置为0。对于结构体,也是类似,是将所有的成员均重置为初始值。delete对于映射类型几乎无影响,因为键可能是任意的,且往往不可知。所以如果你删除一个结构体,它会递归删除所有非mapping的成员。当然,你是可以单独删除映射里的某个键,以及这个键映射的某个值。 需要强调的是delete a的行为更像赋值,为a赋予一个新对象。我们来看看下文的示例: pragma solidity ^0.4.0; contract DeleteExample { uint data; uint[] dataArray; function f() { //值传递 uint x = data; //删除x不会影响data delete x; //删除data,同样也不会影响x,因为是值传递,它存的是一份原值的拷贝。 delete data; //引用赋值 uint[] y = dataArray; //删除dataArray会影响y,y也将被赋值为初值。 delete dataArray; //下面的操作为报错,因为删除是一个赋值操作,不能向引用类型的storage直接赋值从而报错 //delete y; } } 通过上面的代码,我们可以看出,对于值类型,是值传递,删除x不会影响到data,同样的删除data也不会影响到x。因为他们都存了一份原值的拷贝。 而 对于复杂类型略有不同,复杂类型在赋值时使用的是引用传递。删除会影响所有相关变量。比如上述代码中,删除dataArray同样会影响到y。 由于delete的行为更像是赋值操作,所以不能在上述代码中执行delete y,因为不能对一个storage的引用赋值 类型推断(Type Deduction) 为了方便,并不总是需要明确指定一个变量的类型,编译器会通过第一个向这个对象赋予的值的类型来进行推断。 uint24 x = 0x123; var y = x; 函数的参数,包括返回参数,不可以使用var这种不指定类型的方式。 需要特别注意的是,由于类型推断是根据第一个变量进行的赋值。所以代码for (var i = 0; i < 2000; i++) {}将是一个无限循环,因为一个uint8的i的将小于2000。 pragma solidity ^0.4.4; contract Test{ function a() returns (uint){ uint count = 0; for (var i = 0; i < 2000; i++) { count++; if(count >= 2100){ break; } } return count; } } 2.单位 货币单位(Ether Units)一个字面量的数字,可以使用后缀wei,finney,szabo或ether来在不同面额中转换。不含任何后缀的默认单位是wei。如2 ether == 2000 finney的结果是true。 pragma solidity ^0.4.0; contract EthUnit{ uint a; function f() returns (bool){ if (2 ether == 2000 finney){ return true; } return false; } } 时间单位(Time Units) seconds,minutes,hours,days,weeks,years均可做为后缀,并进行相互转换,默认是seconds为单位。 默认规则如下: 1 == 1 seconds 1 minutes == 60 seconds 1 hours == 60 minutes 1 days == 24 hours 1 weeks = 7 days 1 years = 365 days 如果你需要进行使用这些单位进行日期计算,需要特别小心,因为不是每年都是365天,且并不是每天都有24小时,因为还有闰秒。由于无法预测闰秒,必须由外部的oracle来更新从而得到一个精确的日历库(内部实现一个日期库也是消耗gas的)。 后缀不能用于变量。如果你想对输入的变量说明其不同的单位,可以使用下面的方式。 pragma solidity ^0.4.0; contract DeleteExample{ function nowInSeconds() returns (uint256){ return now; } function f(uint start, uint daysAfter) { if (now >= start + daysAfter * 1 days) { } } } 3.语言内置特性 特殊变量及函数(Special Variables and Functions) 有一些变量和函数存在于全局上下文中。主要用来提供一些区块链当前的信息。区块和交易的属性(Block And Transaction Properties)block.blockhash(uint blockNumber) returns (bytes32),给定区块号的哈希值,只支持最近256个区块,且不包含当前区块。block.coinbase (address) 当前块矿工的地址。block.difficulty (uint)当前块的难度。block.gaslimit (uint)当前块的gaslimit。block.number (uint)当前区块的块号。block.timestamp (uint)当前块的时间戳。msg.data (bytes)完整的调用数据(calldata)。msg.gas (uint)当前还剩的gas。msg.sender (address)当前调用发起人的地址。msg.sig (bytes4)调用数据的前四个字节(函数标识符)。msg.value (uint)这个消息所附带的货币量,单位为wei。now (uint)当前块的时间戳,等同于block.timestamptx.gasprice (uint) 交易的gas价格。tx.origin (address)交易的发送者(完整的调用链)msg的所有成员值,如msg.sender,msg.value的值可以因为每一次外部函数调用,或库函数调用发生变化(因为msg就是和调用相关的全局变量)。如果你想在库函数中,用msg.sender实现访问控制,你需要将msg.sender做为参数(就是说不能使用默认的msg.value,因为它可能被更改)。 为了可扩展性的原因,你只能查最近256个块,所有其它的将返回0. 数学和加密函数(Mathematical and Cryptographic Functions) asser(bool condition):如果条件不满足,抛出异常。 addmod(uint x, uint y, uint k) returns (uint): 计算(x + y) % k。加法支持任意的精度。但不超过(wrap around?)2**256。 mulmod(uint x, uint y, uint k) returns (uint): 计算(x * y) % k。乘法支持任意精度,但不超过(wrap around?)2**256。 keccak256(...) returns (bytes32):使用以太坊的(Keccak-256)计算HASH值。紧密打包。 sha3(...) returns (bytes32):等同于keccak256()。紧密打包。sha256(...) returns (bytes32):使用SHA-256计算HASH值。紧密打包。ripemd160(...) returns (bytes20):使用RIPEMD-160计算HASH值。紧密打包。ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address):通过签名信息恢复非对称加密算法公匙地址。如果出错会返回0 revert():取消执行,并回撤状态变化。 需要注意的是参数是“紧密打包(tightly packed)”的,意思是说参数不会补位,就直接连接在一起的。下面来看一个例子: keccak256("ab", "c") keccak256("abc") //hex keccak256(0x616263) keccak256(6382179) //ascii keccak256(97, 98, 99) 上述例子中,三种表达方式都是一致的。 如果需要补位,需要明确的类型转换,如keccak256("\x00\x12")等同于keccak256(uint16(0x12)) 需要注意的是字面量会用,尽可能小的空间来存储它们。比如,keccak256(0) == keccak256(uint8(0)),keccak256(0x12345678) == keccak256(uint32(0x12345678)) 在私链(private blockchain)上运行sha256,ripemd160或ecrecover可能会出现Out-Of-Gas报错。因为它们实现了一种预编译的机制,但合约要在收到第一个消息后才会存在。向一个不存在的合约发送消息,非常昂贵,所以才会导致Out-Of-Gas的问题。一种解决办法是每个在你真正使用它们前,先发送1 wei到这些合约上来完成初始化。在官方和测试链上没有这个问题。 地址相关(Address Related) <address>.balance (uint256):Address的余额,以wei为单位。<address>.transfer(uint256 amount):发送给定数量的ether,以wei为单位,到某个地址。失败时抛出异常。<address>.send(uint256 amount) returns (bool):发送给定数量的ether,以wei为单位,到某个地址。失败时返回false。 <address>.call(...) returns (bool): 发起底层的call调用。失败时返回false。 <address>.callcode(...) returns (bool): 发起底层的callcode调用,失败时返回false。 <address>.delegatecall(...) returns (bool): 发起底层的delegatecall调用,失败时返回false。 使用send方法需要注意,调用栈深不能超过1024,或gas不足,都将导致发送失败。使用为了保证你的ether安全,要始终检查返回结果。当用户取款时,使用transfer或使用最佳实践的模式。 合约相关 this(当前合约的类型)当前合约的类型,可以显式的转换为Addressselfdestruct(address recipt):销毁当前合约,并把它所有资金发送到给定的地址。 另外,当前合约里的所有函数均可支持调用,包括当前函数本身。转载地址:https://blog.csdn.net/yhc166188/article/details/79826594 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
很好
[***.229.124.182]2024年04月06日 07时22分06秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
Apache HTTPD 换行解析漏洞
2019-04-29
Vulhub Apache HTTPD 多后缀解析漏洞
2019-04-29
CTFshow 反序列化
2019-04-29
CTFSHOW SSRF
2019-04-29
[BJDCTF2020]Mark loves cat
2019-04-29
[网鼎杯 2020 朱雀组]phpweb
2019-04-29
[BJDCTF2020]Cookie is so stable
2019-04-29
[SUCTF 2019]Pythonginx
2019-04-29
[极客大挑战 2019]RCE ME
2019-04-29
HackTheBox-------ScriptKiddie
2019-04-29
Shell学习
2019-04-29
[Zer0pts2020]Can you guess it?
2019-04-29
Jenkins资料整理
2019-04-29
ArrayList源码常用方法注意点
2019-04-29
MySQL资料整理
2019-04-29
Redis常用文章整理
2019-04-29
RocketMQ资料整理
2019-04-29
慢sql统计
2019-04-29
基于webRTC的1V1在线视频聊天(网页版DEMO)
2019-04-29
Disconf数据安全保护设计方案
2019-04-29