从今天开始,我将会学习以太坊智能合约开发的solidity,这里,我将会将我学习过程中遇到的一些值得注意的知识点记录下来并且不断更新。
- string类型数据作为参数的时候,需要加上memory/calldata.
- Calldata/memory/storage区别
一般只有外部函数的参数(不包括返回参数)被强制指定为calldata。这种数据位置是只读的,不会持久化到区块链
memory存储位置同我们普通程序的内存类似,即分配,即使用,动态分配,越过作用域即不可被访问,等待被回收。
而对于storage的变量,数据将永远存在于区块链上。
强制指定的数据位置:
• 外部函数的参数(不包括返回参数): calldata,效果跟 memory 差不多
• 状态变量: storage
默认数据位置:
• 函数参数(包括返回参数): memory
• 所有其它局部变量: storage - 输出的时候需要查看才使用VIEW关键字,如果是输入不需要.
- Function()必须有
{private/public/internal/external}任意选一
[pure | constant | view | payable]任意多个
[returns()]可有可没有 - Overload:函数的名字相同,输入的参数(类型/数量)不同,返回值参数不同不算重载
- Address(16进制)类型数据实际上是uint160(10进制),两者都不能传入除地址外的数字
- 传参数:可以按序传入(X,Y),也可以({参数2:Y,参数1:X…})
- 函数返回值可以有名字
- 函数可以有多返回值
- 变量不可以重复定义,函数内定义的变量只在函数内部起作用
- Private只能被合约独立使用,不能被继承;Internal的函数只可以在合约内或者是继承的合约内被调用(只能在合约函数中调用,部署后的界面不可以);External恰好相反,只可以在合约外部调用;Public可以使函数在外部调用(既在控制台中可以调用)
- Public只可以修饰全局变量
- Constant从0.5.0开始被移除了,不可以修饰函数,只可以修饰全局变量
- Require若条件不通过,则会回滚,不会执行之后的语句
- Modifier关键字在定义时候为:
modifier name{
判断语句;
_;//代入判断后需要执行的语句
}
使用时为:
Function name1() public name{
需要判断后执行的语句;
} - 映射函数定义:mapping(type1 => type2) name;
使用的时候name[type1]----->type2
如果没有产生映射,则会返回0 - Modifier可以带参数,modifier可以增加代码重用性,一般是用来和require等判断语句配合
- 使用Modifier的函数可以有多个modifier用来函数的嵌入:
Modifier modifier1{
x1;
_;
x2;
}
Modifier modifier2{
y1;
_;
y2;
}
Function name() public modifier1 modifier2{
z
}
但执行的顺序会是 x1->y1->z->y2->x2
19. 可以传递继承
20. 合约中不能嵌套合约
21. 继承时函数和变量区别:
Pure:不读取也不修改全局变量,使用本地计算机计算函数,不消耗gas。
View:只读取全局变量的值而不修改,不消耗gas。
Constant:在函数中和view相同,在全局变量中,只用于bytes1-bytes32,uint,int,string,其数据不允许被修改。
Payble:在转账的时候必须要的关键字
22. public修饰符默认生成get方法,供我们外部调用,但只有在0.4.16版本可以自己构建相应的get函数
23. 在继承中,变量重载可以直接覆盖父类同名变量,函数重载的函数定义可以和父类完全一致------在同一合约中的重载,必须使输入的参数不同(数量,类型),返回参数并不能重载
24. (此种多继承重载仅在0.6.0版本前可以使用)父母均有同名属性或者函数,孩子继承后者的属性值(如下继承的是mother的属性值)
Contract children is father,mother{
}
如果孩子也有同名该属性或者函数,最后结果以孩子定义的为准
25. 局部变量,参数默认类型是memory,仅在当前函数起作用。全局变量默认类型是storage,存储在区块链上,在整个合约起作用。
26.在函数内部定义可变长度数组,类型是storage,相当于指针,如果指向全局变量,相当于直接对这个全局变量修改
pragma solidity ^0.6.0;
contract storageTest{
uint[] arrx;///全局变量默认storage
function init(uint[] memory temp) public{
uint[] storage arry=arrx;//令arry指向arrx
arrx=temp;
arry[0]=100;
}
function getElement() public view returns(uint){
return arrx[0];//修改arry[0],也会使arrx[0]发生改变
}
function getLength() public view returns(uint){
return arrx.length;
}
}
27.结构体定义和使用
struct不可以递归
pragma solidity ^0.6.0;
contract structTest{
struct student{
uint grade;
string name ;
uint id;
//student[] stu1;可以有变长结构体数组
//mapping(uint => student) stu2;可以有结构体映射
}
function init() public view returns(uint,string memory,uint){
//右边结构体初始化是memory类型,需要将student转换为memory
student memory stu=student({grade:100,id:20225425,name:"tzy"});
//两种初始化结构体的方式
//student memory stu=student(100,"tzy",20225425);
return (stu.grade,stu.name,stu.id);多返回值用括号括好
}
}
28.struct
- struct初始化的时候可以忽略mapping类型的初始化
- memory类型定义的结构体,无法操作结构体中的mapping数据,storage类型才能调用
- 结构体作参数,函数要是internal类型
pragma solidity ^0.6.0;
contract structTest1{
struct student{
uint id;
string name;
//映射指的是name->course->grade
mapping(string => mapping(string => uint))grade;
}
student temp;//结构体全局变量默认是storage类型
function init() public{
//结构体在函数定义时,等号右边时memory类型的,需要在左边用memory修饰
student memory stu=student(20225425,"tzy");
temp=stu;
temp.grade["tzy"]["data structure"]=100;
}
function getInfo() public view returns(uint,string memory,uint){
return (temp.id,temp.name,temp.grade["tzy"]["data structure"]);
}
//结构体作为参数,函数只能内部调用(internal)
function test(student memory temp1) internal pure returns(uint,string memory){
return (temp1.id,temp1.name);
}
function transmit(uint _id,string memory _name) public view returns(uint,string memory){
student memory temp2;
temp2.id=_id;
temp2.name=_name;
return test(temp2);
}
}
调用后结果如下:
29.storage作用类似于引用
pragma solidity ^0.6.0;
contract storageOfStruct{
struct student{
uint id;
string name;
}
student stu;//storage类型
function changeStruct(student storage stu1) internal{
//用stu1指向stu,用stu2指向stu1
student storage stu2;
stu2=stu1;
stu2.name="tzyyy";
}
function transmitStruct(uint _id,string memory _name) public {
stu.id=_id;
stu.name=_name;
changeStruct(stu);
}
function getInfo() public view returns(string memory){
// 对stu2进行操作,而stu的name值发生了改变,证明storage作用类似于引用
return stu.name;
}
}
结果如下:
其在区块中的存放如下:
but!,there is one exception:
因为所有结构体都放在memory中进行传递的话太浪费空间了,solidity会自动转换成引用的方式
pragma solidity 0.6.0;
contract memory2Memory{
struct student{
uint id;
string name;
}
function changeStruct(student memory stu1) internal{
student memory stu2;
stu2=stu1;
stu2.name="tzyyy";
}
function transmitStruct(uint _id,string memory _name) public returns(string memory){
//memory和memory互相传递的时候,效果和storage之间的传递一样(也是传引用)
student memory stu;
stu.id=_id;
stu.name=_name;
changeStruct(stu);
return(stu.name);
}
}
30.enum关键字的注意点和使用
- enum定义时必须要要有初始值
- enum中不能输入汉字
- enum语句末尾不加分号
- enum相当于是一个类型相同元素不同的容器,主要作用是配合require完成状态转移的功能
pragma solidity ^0.6.0;
contract enumTest{
enum courses{compusoryCourse,electiveCourse,others}
//初始只能选必修课
courses state=courses.compusoryCourse;
function fristChoose() public returns(string memory){
require(state==courses.compusoryCourse);
//选完必修课后可以选选修课
state=courses.electiveCourse;
return "You can only choose compusory course!";
}
function secondChoose() public returns(string memory){
require(state==courses.electiveCourse);
state=courses.others;
return "You can choose elective course!";
}
//courses类型实质上就是uint,标识在eum中的第几个元素(0开始)
function getstate() public view returns(courses,string memory){
require(state==courses.others);
string memory temp="You can choose all the courses now!";
return (state,temp);
}
}