Solidity编程语言基础知识三(Solidity字面常量、智能合约、状态变量、局部变量、函数、函数修改器、事件、异常处理、结构体和枚举类型)

6.Solidity字面常量

        字面常量就是它字面上一个具体的数据值,表现为一条数字或一条文本等,如:69、4.13或HelloWorld。字面常量常用于给变量赋值或参与运算,如给变量赋值:int num = 69、string str = "HelloWorld";如参与运算:10+5、10/5。

Solidity字面常量详细介绍如下

字面常量名称

描述

特殊说明

布尔字面常量

布尔字面常量是指对与错。在Solidity语言中分别用true与false表示对与错。

数值字面常量

数值字面常量包括十进制整数字面常量和十进制小数字面常量。

十进制整数字面常量是指数学中的正整数、0、负整数。

十进制小数字面常量是指数学中的正小数、负小数。

为了提高可读性,在数字之间支持加上下划线,例如:54_34等同于5434、23_33.23_33等同于2333.2333。

字符串字面常量

Solidity语言采用的是ASCII字符集和UTF-8字符集,若字符串只包含ASCII字符集的字符,则字符串需要用双引号或单引号引起来,如:"hello World";若字符串含有UTF-8字符集的字符(一般是指汉字或汉语标点符号),则字符串必须以关键字unicode开头,后面紧跟着用单引号或双引号引起来的字符串,如:uincode"你今天真nice"。

在Solidity语言中,一些字符有了特定含义,如:双引号(")和单引号('),不能够直接输出这些字符,如:要输出一个双引号或单引号,不能直接写为"""或"'",否则编译器会报错。要想将这些字符按要求正确的输出需要转义,如:写为"\""或"\'",像这样的字符被称为转义字符。在Solidity中转义字符有:

单引号:\'

双引号:\"

反斜杠:\\

退格:\b

换页:\f

回车:\r

制表符:\t

垂直制表符:\v

表示十六进制值并插入适当的字节:\xNN

表示unicode值并插入UTF-8序列:\uNNNN

注意:ASCII字符集的字符占用一个字节,UTF-8字符集占用3个字节。

为了提高可读性,字符串字面常量可以分为多个连续的部分,链接的部分用空格链接,如:"HelloWorld"可分为"Hello" "World",unicode"你今天真nice"可分为unicode"你今天" unicode"真nice"。

十六进制字面常量

十六进制字面常量是以0x开头,后面紧跟着字符串,如:0x1122ff。

注意:十六进制的一个字符占用4个位,一个字节占用8个位,因此十六进制的字符个数必须是偶数,否则编译器会报错,如0x22334,编译器不会编译通过。

为了提高可读性,十六进制字面常量支持在字符间加上下划线,如:0x11_22_33等同于0x112233;

十六进制字符串字面常量

十六进制字符串字面常量是指以关键字hex开头,后面紧跟着单引号或双引号引起来的字符串,如:hex"112233"。

注意:十六进制的一个字符占用4个位,一个字节占用8个位,因此十六进制的字符个数必须是偶数,否则编译器会报错,如hex"22334",编译器不会编译通过。

为了提高可读性,十六进制字符串字面常量可以分为多个连续的部分,链接的部分用空格链接,如:hex"11" hex"22" hex"33"等同于hex"112233"。

地址字面常量

是一个特殊的十六进制字面常量,需要通过地址校验,如:0x5B38Da6a701c568545dCfcB03FcB875f56beddC4。

数组字面常量

在方括号中包含一个或多个逗号分隔的表达式。如:[1,2,3,4]、['a', 'b', 'c', 'd']或[f(2), g(3), h(4)]。

数组的类型是由列表上的第一个表达式的类型决定,其他表达式需要隐式地转换成此类型,如果不可以转换,将出现类型错误。

如:[-8, -3, 8, 9]数组字面常量,其第一个表达式-8是int8类型,因此剩余的表达式必须能隐式地转换成int8类型,否则编译器会报错。

注意:像[-8, -3, 8, 1000]这样第一个表达式-8是int8类型,第四个表达式1000是int16类型的数组字面常量,由于存在了两个类型,这是不允许的,因此编译器会报错。由于int16的取值范围(-2^16~2^16-1)包含int8的取值范围(-2^8~2^8-1),可以将第一个表达式-8显示的转换成int16类型来解决上述问题,如:[int16(-8), -3, 8, 1000]。(隐式转换和显式转换在“8.8.数据类型转换”章节详细介绍)

7.Solidity合约

        Solidity合约类似于Java语言中的类。每个合约中可以包含状态变量、函数、函数修改器、事件、异常、结构体和枚举类型这7种合约结构,且这7种合约结构可以从其他合约中继承(继承在“16.Solidity继承”章节详细介绍)

7.1.声明合约

contract ContractName{
  // 语句
}

相关字段

描述

contract

声明合约的关键字

ContractName

自定义的合约名称

 7.2.创建合约

        创建合约即实例化合约,类似于Java语言中的创建对象。创建合约有两种方式:常规方式创建合约和加“盐”方式创建合约。

7.2.1.常规方式创建合约

new ContractName(<parameter list>)

相关字段

描述

new

创建合约的关键字

ContractName

声明合约时的合约名称

<parameter list>

与合约的构造函数有关,在声明合约时,若构造函数有设置输入参数,则需要在创建合约时传入值。<parameter list>是指传入的值列表,值间用逗号隔。(本章节可先忽略,在“9.6.构造函数”章节详细介绍)

    // 测试常规方式创建合约的合约
    contract TestCreateContract{
        // 声明状态变量时,创建Test合约
        Test testA = new Test();
        // 用于将在createContract函数内创建的Test合约赋值于testB状态变量
        Test testB ;

        // 在函数内创建合约
        function createContract() public{
            // 创建Test合约并赋值给testB变量
            testB = new Test();
        }

        // 通过testA和testB状态变量调用Test合约中的testFun函数
        function callTestFun() public view returns(string memory, string memory){
            return (testA.testFun(), testB.testFun());
        }
    }

    // 声明Test合约
    contract Test{
        // 输出“Test合约创建成功”文案
        function testFun() public pure returns(string memory){
            return unicode"Test合约创建成功";
        }
    }

 7.2.2.加“盐”方式创建合约

        在上述创建合约时,将根据创建合约的地址和此地址计数器(nonce)来计算新合约的地址。但此方法创建的合约地址依赖于交易者交易数量不断增长的nonce变量,这种方式很难确定一个未来要部署的合约地址(即合约地址在部署后才可获得,部署前很难获得)。

        要想在合约部署前就获取合约地址,需要使用另一种机制来生成新合约的地址(这种机制不再使用计数器nonce),这种机制生成的合约地址有4部分决定,如下表格,然后对这四部分做keccak256哈希后转换成address类型。

生成合约的部分

描述

bytes1(0ff)

一个常数,为了避免加“盐”方式创建合约生成的地址与常规方式创建合约生成的地址发生冲突冲突。

创建者地址

saltValue

盐值,一个自定义的bytes32值。

待部署合约的字节码

是指待部署合约的创建时字节码和欲传入此合约构造函数的参数值做编码后的keccak256哈希。

上述方法是提前获取合约的地址,但没有部署合约。使用加“盐”方式部署合约的方式如下:

new ContractName{salt: saltValue}(<parameter list>)

相关字段

描述

new

创建合约的关键字

ContractName

声明合约时的合约名称

{salt: saltValue}

使用加“盐”方式部署合约的盐值,saltValue是一个bytes32值

<parameter list>

与合约的构造函数有关,在声明合约时,若构造函数有设置输入参数,则需要在创建合约时传入值。<parameter list>是指传入的值列表,值间用逗号隔。(本章节可先忽略,在“9.6.构造函数”章节详细介绍)

    // 测试加“盐”创建合约的合约
    contract TestSaltContract{
        
        // 两种方式获取加“盐”合约的地址
        function createDSalted() public returns(address, address, string memory){
            /*不部署只获取合约地址*/
            // 常量
            bytes1 bytes1Constant = 0xff;
            // 创建者地址
            // this表示当前合约
            address creatorAddress = address(this);
            // 盐值
            // keccak256(param):计算输入参数param的哈希,返回值为bytes32数据类型
            bytes32 saltValue = keccak256(unicode"自定义盐值");
            // 待部署合约的字节码
            // type(C).creationCode:获取合约C的创建时字节码。此处是获取Test合约的运行时字节码
            // abi.encode(<param list>):对输入的参数列表<param list>执行abi编码。目前Test合约构造函数的输入参数为空,所以<param list>为空
            // abi.encodePacked(<param list):对输入的参数列表<param list>执行紧打包编码
            bytes32 bytecode = keccak256(abi.encodePacked(type(Test).creationCode,abi.encode()));
            // 对上述4部分紧打包编码后执行keccak256哈希
            bytes32 hash = keccak256(abi.encodePacked(bytes1Constant, creatorAddress, saltValue, bytecode));
            // 数据类型转化,获取Test合约的合约地址
            address addrA = address(uint160(uint(hash)));

            /*部署并获取合约地址*/
            // 部署Test合约
            Test test = new Test{salt: saltValue}();
            // 将Test合约转化为地址类型
            address addrB = address(test);

            // 判断addrA和addrB地址是否相同
            string memory isEqual;
            if(addrA == addrB){
                isEqual = unicode"地址相同";
            }else{
                isEqual = unicode"地址不相同";
            }

            return (addrA, addrB, isEqual);
        }
    }

    // 待部署合约
    contract Test{
        // 输出“Test合约创建成功”文案
        function testFun() public pure returns(string memory){
            return unicode"Test合约创建成功";
        }
    }

8.Solidity变量

        变量是用于存储信息的"容器",如:unit num = 10,声明unit类型且名称为num的变量,并将10存储于num变量。

8.1.变量分类

        根据变量的作用和声明位置可以将变量分为全局变量、状态变量、局部变量。

  • 全局变量

        声明在全局工作区中的变量被称为全局变量。全局变量是Solidity语言预制好的,提供有关区块链和交易属性的信息。(全局变量在“20.Solidity全局属性”章节详细介绍)

  • 状态变量

        声明在合约内,在函数和函数修改器之外的变量被称为状态变量。状态变量在合约内任何地方都可访问,状态变量的值会被永久存储在以太坊区块链上相应账户的account storage空间中。

  • 局部变量

        声明在函数或函数修改器内的变量被称为局部变量。局部变量仅在函数或函数修改器执行过程中有效,函数或函数修改器执行结束后局部变量便失效。输入参数和返回参数也是局部变量。

下图红色圈为状态变量、黄色圈为局部变量、青色圈为全局变量

 8.2.状态变量和局部变量的声明

  • 声明状态变量
<dataType> <visibility> [constant | immutable] <dataLocation> [override] varName = value

相关字段

描述

<dataType>

是指数据类型,Solidity语言提供了值类型和引用类型两大类型。

<visibility>

是指状体变量可见性,有public、internal、private三种。若不写则默认为internal。

[constant | immutable]

constant是指声明常量的关键字。

immutable是声明不可变量的关键字,也是常量的一种。

<dataLocation>

是指数据位置,来说明数据存储的位置。数据位置种类分为storage(称为存储)、memory(称为内存)、calldata(称为调用数据)三种位置。状态变量只使用storage且默认也是storage,因此在声明状态变量时,不必且不可标注数据位置。

[override]

在继承中用到的关键字,被override修饰的状态变量要重写父合约中对应的函数(本章节可先忽略,在“16.1.1.重写函数”章节详细介绍)

varName

自定义的变量名称。

value

是指为变量赋的值,若不写则会初始化为默认值。

  • 声明局部变量
<dataType> <dataLocation> varName = value

相关字段

描述

<dataType>

是指数据类型,包括值类型和引用类型。

<dataLocation>

是指数据位置,来说明数据存储的位置。数据位置分为storage(称为存储)、memory(称为内存)、calldata(称为调用数据)三种位置。

varName

自定义的变量名称。

value

是指为变量赋的值,若不写则会初始化为默认值。

 8.3.变量命名规则

  • 变量名称只能使用字母、数字、下划线、美元符号组成。
  • 变量名必须以字母、下划线开头。
  • 变量名区分大小写。
  • 不可使用Solidity语言的保留关键字作为变量名。

 8.3.数据类型

        Solidity语言是一种静态类型的语言(即编译时已知变量类型),因此需要在编写程序时指定变量的类型。Solidity语言提供了值类型和引用类型两大数据类型。

        值类型:由于值类型占用的存储空间固定,因此值类型的变量按值传递(即一个变量的值赋值给另一个变量,则这个值会复制一份赋值给后者变量,其中一个变量的值发生改变不会影响另一个变量的值)。

        引用类型:由于引用类型占用的存储空间不固定,为节省存储空间,引用类型的变量按引用传递(即一个变量的值赋值给另一个变量,则后者变量用引用直接指向前者变量的值,其中一个变量值的某个元素发生改变另一个变量值对应的元素跟随变化,但是其中一个变量值整体发生改变另一个变量值不会发生改变)。

 

    // 测试值类型和引用类型的合约,值类型和引用类型赋值流程如下图:
    contract Test{
        function fun() public pure returns(uint, uint, uint[5] memory, uint[5] memory, uint[5] memory){
            // uint是值类型
            uint numA = 10;
            // 将numA变量的值复制一份赋给numB变量,此时两个变量的值都是10
            uint numB = numA;
            // 将numA和numB两个变量分别赋值为20和30
            numA = 20;
            numB = 30;

            // uint[5]数组是引用类型
            uint[5] memory numsA = [uint(1),2,3,4,5];

            // 将numsA变量赋值给numsB变量,此时numsA和numsB两个变量的引用都指向[uint(1),2,3,4,5]
            uint[5] memory numsB = numsA;
            // 给numsB变量重新赋值为[uint(6),7,8,9,10]
            // 则numsA变量的引用还是指向[uint(1),2,3,4,5],numsB变量的引用指向[uint(6),7,8,9,10]
            numsB = [uint(6),7,8,9,10];

            // 将numsA变量赋值给numsC变量,此时numsA和numsC两个变量的引用都指向[uint(1),2,3,4,5]
            uint[5] memory numsC = numsA;
            // 通过numsA变量将数组的第二个元素设置为20
            // 通过numsC变量将数组的第三个元素设置为30
            numsA[1] = 20;
            numsC[2] = 30;

            // numA变量的输出为20
            // numB变量的输出为30
            // numsA变量的输出为[1,20,30,4,5]
            // numsB变量的输出为[6,7,8,9,10]
            // numsC变量的输出为[1,20,30,4,5]
            return (numA, numB, numsA, numsB, numsC);
        }
    }

值类型和引用类型赋值流程如下图

8.4.1. 值类型

8.4.1.1.布尔类型

名称

描述

关键字

bool

存储大小

1字节

值范围

布尔字面常量

默认值

false

支持的运算符

非(!)、与(&&)、或(||)、等于(==)、不等于(!=)

示例

bool bl = false;

8.4.1.2.无符号整型

名称

描述

关键字

uintN(N是指8、16、24 ... 256,分别表示8位无符号整型、16位无符号整型、24位无符号整型 ... 256位无符号整型。uint是uint256的别名。)

存储大小

和N有关,uint8大小为1字节、uint16大小为2字节、uint24大小为3字节 ... uint256大小为32字节

值范围

  • uint8、uint16、uint24 ... uint256的值范围分别是0~2^8-1、0~2^16-1、0~2^24-1 ... 0~2^256-1的十进制整数字面常量
  • 不超过相应整型取值范围的十六进制字面常量

默认值

0

支持的运算符

小于不等于(<=)、小于(<)、等于(=)、不等于(!=)、大于不等于(>=)、大于(>)

按位与运算(&)、按位或运算(|)、按位异或运算(|)、按位反运算(|)

左移位(<<)、右移位(>>)

加(+)、减(-)、乘(*)、除(/)、取余(%)、幂(**)

示例

uint8 num = 10;

8.4.1.3.有符号整型

名称

描述

关键字

intN(N是指8、16、24 ... 256,分别表示8位有符号整型、16位有符号整型、24位有符号整型 ... 256位有符号整型。int是int256的别名。)

存储大小

和N有关,int8大小为1字节、int16大小为2字节、int24大小为3字节 ... int256大小为32字节

值范围

  • int8、int16、int24 ... int256的值范围分别是-2^8~2^8-1、-2^16~2^16-1、-2^24~2^24-1 ... -2^256~2^256-1的十进制整数字面常量
  • 不超过相应整型取值范围的十六进制字面常量

默认值

0

支持的运算符

小于不等于(<=)、小于(<)、等于(=)、不等于(!=)、大于不等于(>=)、大于(>)

按位与运算(&)、按位或运算(|)、按位异或运算(|)、按位反运算(|)

左移位(<<)、右移位(>>)

加(+)、减(-)、乘(*)、除(/)、取余(%)、幂(**)、一元运算负(-)

示例

iny8 num = -10;

8.4.1.4.无符号定长浮点型

名称

描述

关键字

ufixedMxN(M是指8、16、24 ... 256,表示该类型占用的位数;N可以是从0至80之间的任意整数,表示可用的小数位数。ufixed是ufixed128x19的别名。)

存储大小

和M有关,ufixed8xN大小为1字节、ufixed16xN大小为2字节、ufixed24xN大小为3字节 ... ufixed256xN大小为32字节

值范围

十进制小数字面常量

默认值

0

支持的运算符

小于不等于(<=)、小于(<)、等于(=)、不等于(!=)、大于不等于(>=)、大于(>)

加(+)、减(-)、乘(*)、除(/)、取余(%)、幂(**)

示例

ufixed128x19 decimal;

注意:

Solidity语言还没有完全支持无符号定长浮点型。可以声明定长浮点型的变量,但不能给它们赋值或把它们赋值给其他变量。

8.4.1.5.有符号定长浮点型

名称

描述

关键字

fixedMxN(M是指8、16、24 ... 256,表示该类型占用的位数;N可以是从0至80之间的任意整数,表示可用的小数位数。fixed是fixed128x19的别名。)

存储大小

和M有关,fixed8xN大小为1字节、fixed16xN大小为2字节、fixed24xN大小为3字节 ... fixed256xN大小为32字节

值范围

十进制小数字面常量

默认值

0

支持的运算符

小于不等于(<=)、小于(<)、等于(=)、不等于(!=)、大于不等于(>=)、大于(>)

加(+)、减(-)、乘(*)、除(/)、取余(%)、幂(**)

示例

fixed128x19 decimal;

注意:

Solidity语言还没有完全支持有符号定长浮点型。可以声明定长浮点型的变量,但不能给它们赋值或把它们赋值给其他变量。

8.4.1.6.普通地址类型

名称

描述

关键字

address

存储大小

20字节

值范围

地址字面常量

默认值

0x0000000000000000000000000000000000000000

支持的运算符

小于不等于(<=)、小于(<)、等于(=)、不等于(!=)、大于不等于(>=)、大于(>)

示例

address addr = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;

8.4.1.7.支付地址类型

名称

描述

关键字

address payable

存储大小

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Solidity是一种智能合约语言,它是以太坊平台上的主要编程语言之一。以下是Solidity的教程: 1. 智能合约的基础结构 Solidity智能合约由两个部分组成:状态变量函数状态变量是合约内部存储的数据,而函数是合约内部的代码,用于处理状态变量或执行其他操作。 2. Solidity的数据类型 Solidity支持各种数据类型,包括布尔值、整型、地址、字符串、数组、结构体等。例如: - 布尔值:bool - 整型:int、uint - 地址:address - 字符串:string - 数组:array - 结构体:struct 3. Solidity函数 函数Solidity合约最重要的部分之一。函数可以接受参数,也可以返回值。例如: ``` function add(uint a, uint b) public returns (uint) { return a + b; } ``` 这个函数接受两个整数作为参数,并返回它们的和。 4. Solidity的控制流 Solidity支持各种控制流结构,包括if、for、while、do-while等。例如: ``` function isEven(uint x) public returns (bool) { if (x % 2 == 0) { return true; } else { return false; } } ``` 这个函数接受一个整数作为参数,并返回它是否为偶数。 5. Solidity事件 事件Solidity合约的一种通知机制,它可以向外部应用程序发送消息。例如: ``` event Transfer(address indexed _from, address indexed _to, uint _value); ``` 这个事件表示在合约发生了一次转账操作,它包含了发送方地址、接收方地址和转账金额。 6. Solidity的继承 Solidity支持继承,一个合约可以从另一个合约继承状态变量函数。例如: ``` contract A { uint public x = 1; } contract B is A { uint public y = 2; } ``` 在这个例子,合约B从合约A继承了状态变量x,并且定义了自己的状态变量y。 以上就是Solidity的基础教程,希望对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值