Solidity03 Solidity变量简述
文章目录
- 一、变量简述
- 1.1 状态变量
- 1.2 局部变量
- 1.3 全局变量
- 1.4 注意问题
- 二、变量可见性
- 2.1 public
- 2.2 private
- 2.3 internal
- 2.4 默认可见性
- 2.5 可见性的用处
- 三、变量初始值
- 3.1 值类型初始值
一、变量简述
变量是指可以保存数据的内部存储单元,里面的数据可以在程序运行时引用或者修改。
变量都有一个名字,称为变量名。变量名是由字母、数字或下划线“_”组成的字符串,但不能包含空格或其它的特殊字符,也不能以数字开头。比如正确的变量名:symbol、totalSupply、_name、amount1 等等。
Solidity
是一种静态类型的编程语言,它要求在变量声明时,必须明确指定变量的类型。
变量声明时,如果没有赋给初值,都会有一个基于其类型的默认值,比如 int 类型的默认值为 0。
Solidity
提供了 3 种类型的变量:状态变量
、局部变量
、全局变量
。
1.1 状态变量
- 状态变量是指在智能合约中声明的持久化存储的变量。它存在于合约的整个生命周期,直到合约被销毁。
- 状态变量的变动是要记录在区块链上的,永久存储,也就是通常所说的“数据上链”。
- 状态变量在合约的不同函数之间共享,可以通过调用函数来读取或修改它的数据值。
- 状态变量类似于其它编程语言中“结构体(go)或类(java)”的成员变量。
在 Solidity
中,状态变量通常在合约的顶层进行声明,并且定义在全部函数之外。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract StatusVar {
uint256 myStatus = 999; // 声明状态变量
function getStatus() public view returns(uint256) {
return myStatus; // 使用状态变量
}
}
这里首先声明了一个状态变量 myStatus,然后在函数 getStatus 中,把它作为函数的返回值。
运行合约,调用函数 getStatus,输出结果:
0: uint256: 999
1.2 局部变量
- 局部变量是指在函数内部声明的变量,其作用域仅限于该函数内部。
- 局部变量的生命周期仅限于该函数的执行过程中,函数执行完毕后,局部变量将被销毁,它的值也将无法使用。
- 局部变量与状态变量的区别在于:状态变量的数据是要上链的,而局部变量不上链。
- 声明局部变量的语法与状态变量类似,只是要在函数的内部进行声明。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract LocalVar {
function sum() public pure returns(uint256){
uint256 a = 1; // 声明局部变量 a
uint256 b = 2; // 声明局部变量 b
uint256 result = a + b; // 声明局部变量 result,并使用局部变量a, b
return result; // 使用局部变量 result 作为返回值
}
}
运行合约,调用函数 sum,输出结果:
0: uint256: 3
1.3 全局变量
全局变量是指在合约的顶层预先定义的特殊变量,用于提供有关区块链和交易属性的信息。
全局变量是由 Solidity
语言本身提供,用户无权定义或者修改,但可以直接在任何位置使用。
例如,常用的全局变量有:当前区块的时间戳 block.timestamp、区块高度 block.number、函数调用者地址 msg.sender 等。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract GlobalVar {
// 返回当前函数调用所在的区块号、时间戳、调用者地址
function getGlobalVars() public view returns(uint,uint,address,uint){
return (block.number, block.timestamp, msg.sender,msg.sender.balance);
}
}
运行以上合约,调用函数 getGlobalVars,输出下面的结果:
0:uint256: 12 // 当前区块号
1:uint256: 1735107832 // 当前区块的时间戳
2:address: 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 //调用者的地址
3:uint256: 99999999999992885246 //调用者地址的余额
常用的全局变量(更完整的列表请看这个链接):
名称 | 返回 |
---|---|
blockhash(uint blockNumber) returns (bytes32) | 指定区块的哈希值 |
block.coinbase (address payable) | 当前区块的矿工地址 |
block.difficulty (uint) | 当前区块的难度值 |
block.gaslimit (uint) | 当前区块的gaslimit |
block.number (uint) | 当前区块的区块号 |
block.timestamp (uint) | 当前区块的时间戳 |
gasleft() returns (uint256) | 当前剩余的gas |
msg.data (bytes calldata) | 完成 calldata |
msg.sender (address payable) | 消息发送者的地址,也就是函数调用者的地址 |
msg.sig (bytes4) | 消息的函数签名,也就是calldata的前四个字节 |
msg.value (uint) | 消息携带的token数量,也就是函数调用时,同时存入的token量 |
tx.gasprice (uint) | 交易的gas价格 |
tx.origin (address payable) | 交易的发送方地址 |
1.4 注意问题
在声明变量时,应该需要注意以下问题:
- 变量名规范的写法是采用小驼峰形式:每个单词的首字母大写,但整体以小写字母或者下划线“_”开头。比如:symbol、totalSupply、_name。
- 变量名不应该以数字开头,必须以字母或下划线开头。例如:123test 是一个无效的变量名,但是 _123test 是一个有效的变量名。
- 变量名是区分大小写的。例如:Name 和 name 是两个不同的变量。
- 变量声明时,如果没有赋给初值,它就会有一个基于其类型的默认值,比如 int 类型变量的默认值为 0。
Solidity
的变量没有 undefined 或 null 等空值的概念。
二、变量可见性
状态变量的可见性是用来控制状态变量在合约内部或外部的访问权限。
Solidity
为状态变量提供了 3 种可见性修饰符,分别是 public
、private
和 internal
,用于限制状态变量的访问权限。
2.1 public
如果状态变量的可见性声明为 public
,也就是公开变量,那么在合约内部和外部都可以访问这个状态变量。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract PublicVisibility {
uint256 public delta = 8; // 可见性声明为 public
function addDelta(uint256 num) external view returns(uint256) {
uint256 sum = num + delta; // 函数内可以使用状态变量 delta
return sum;
}
}
我们把合约代码复制到 Remix
,进行编译,并部署到区块链上:
我们从外部可以看到状态变量 delta,并且还可以从外部访问它,点击 delta 获得返回值为 8。
2.2 private
如果状态变量的可见性声明为 private
,也就是私有变量,那么就只能在合约内部访问这个状态变量,而不能从外部访问。
通常,私有变量只能由合约内部的函数使用,在合约外部是不可见的,也无法使用。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract PrivateVisibility {
uint256 private delta = 8; // 可见性声明为 private
function addDelta(uint256 num) external view returns(uint256) {
uint256 sum = num + delta; // 函数内可以使用状态变量 delta
return sum;
}
}
我们把合约代码复制到 Remix
,进行编译,并部署到区块链上:
我们从外部看不到状态变量 delta ,所以从外部也就无法访问它。
私有变量将数据隐藏在合约内部,不直接暴露给外部访问,可以防止外部代码直接修改或访问合约的内部状态,提高了合约的安全性。
所以,在 Solidity
编程中,无需外部使用的状态变量,尽可能使用 private
可见性。
2.3 internal
如果状态变量的可见性声明为 internal
,也就是内部变量,那么可以在当前合约和它的继承合约的内部访问这个状态变量,而不能从外部访问。
internal
与 private
相比,就是 internal
变量可以在派生的合约中使用,而 private
变量不能在派生合约中使用,只能在当前的合约中使用。
关于合约继承的相关知识,我们在此处不再延伸。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract InternalVisibility {
uint256 internal delta = 8; // 可见性声明为 internal
function addDelta(uint256 num) external view returns(uint256) {
uint256 sum = num + delta; // 函数内可以使用状态变量 delta
return sum;
}
}
// VarVisibility 继承合约 InternalVisibility
contract VarVisibility is InternalVisibility {
function getDelta() external view returns(uint256) {
return delta; // 继承合约中可以使用状态变量 delta
}
}
我们把合约代码复制到 Remix
,进行编译,并部署到区块链上:
我们从外部无法看到状态变量 delta,所以从外部也无法使用它。但 delta 可以在合约 InternalVisibility 内部和它的继承合约 VarVisibility 中使用。
2.4 默认可见性
如果状态变量在声明的时候,没有指定可见性,那么它的可见性就为默认值 internal
。
也就是说:
uint status = 1;
等于
uint internal status = 1;
2.5 可见性的用处
-
访问控制
通过在状态变量上设置适当的可见性,可以实现对合约功能的访问控制。例如,将敏感数据存储在私有状态变量中,只允许合约的内部特定函数访问,从而确保外部只有通过授权才可以访问这些数据。
-
封装数据
通过将状态变量声明为
private
,可以限制对其数据的直接访问,只能通过合约内部的函数来访问,这样可以有效地封装内部数据,避免外部访问者直接操作状态变量,确保数据的安全性和一致性。另外,除了状态变量外,函数也有可见性的限制,而且函数的可见性增加了一个
external
,我们将在后面的函数中详细讲解。
三、变量初始值
在Solidity
中,声明但没赋值的变量都有它的初始值或默认值。这一讲,我们将介绍常用变量的初始值。
3.1 值类型初始值
-
bool
类型变量的默认值为 false; -
int
类型变量的默认值为 0,包括各种长度的有符号整型 ,比如 int8、int16…; -
uint
类型变量的默认值为 0,包括各种长度的无符号整型,比如 uint8、uint16…; -
string
类型变量的默认值为:“”; -
address
类型变量的默认值为:0x0000000000000000000000000000000000000000
(或address(0)
); -
bytes32
类型变量的默认值为:0x0000…,共 64个 0; -
enum
类型变量的默认值是它列表中的第一个元素。 -
Function 函数
-
internal
: 空白函数 -
external
: 空白函数
-
我们可以通过一个合约来学习变量的默认值:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract VarDefault {
enum status {normal, deleted} // 定义枚举类型
bool public a; // false
int public b; // 0
uint public c; // 0
address public d; // 0x0000...,共 40 个 0
bytes32 public e; // 0x0000...,共 64 个 0
string public f; // ""
status public g; // 0,也就是 status.normal
function fi() internal{} // internal空白函数 //可见性为 `internal` 的变量和函数可以被主合约和子合约访问
function fe() external{} // external空白函数 //可见性为 `external` 的函数只能被第三方合约访问。
}
我们将合约代码复制到 Remix
,进行编译,并部署到区块链上: