Solidity 编程语言:合约结构、类型与修饰符
Solidity 编程语言基础:合约结构、类型与修饰符
Solidity 是以太坊智能合约的主流开发语言,属于静态类型、面向合约的高级语言。本教程将从零讲解合约的基本结构、核心类型系统以及函数与变量的修饰符,帮助初学者快速上手编写安全的智能合约。
合约结构
一个 .sol 文件通常由许可证标识、版本声明、导入语句与合约定义组成。
最小合约骨架
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract HelloWorld {
string public greeting = "Hello, World!";
}
SPDX-License-Identifier:声明代码许可,推荐 MIT。pragma solidity ^0.8.20:指定编译器版本,^表示兼容 0.8.20 及以上但不包含 0.9.0。contract关键字定义合约,类似面向对象中的类。合约内部可以包含状态变量、函数、事件、修饰符、结构体、枚举等。
导入与继承
import "./Other.sol";
import {Symbol} from "./Lib.sol";
contract Child is Parent {
// 可多重继承
}
- 使用
import引入外部文件或合约。 - 通过
is关键字实现继承,支持多重继承,顺序影响方法解析。
类型系统
Solidity 的类型分为值类型与引用类型。值类型在赋值或传参时直接拷贝数据;引用类型需要留意数据位置(memory、storage、calldata)。
值类型
布尔型
bool public isReady = true;
支持 !、&&、||、==、!= 运算。
整数
int8 smallInt; // 有符号 8 位
uint256 bigNumber; // 无符号 256 位(默认)
uint默认为uint256,int默认为int256。- 遵循位数递增步长 8 ,范围
uint8至uint256。 - 运算包括
+、-、*、/、%、**,0.8 起自动检查溢出。
定长浮点数(极少使用)
Solidity 支持 fixed / ufixed,但尚未完全实现,主流合约中几乎不用。
地址类型
address wallet = 0xAb5...;
address payable payee = payable(0xAb5...);
address:普通地址,存储 20 字节的以太坊地址。address payable:可接收以太币,含transfer与send方法。- 常用成员:
.balance查询余额,.call进行低级调用。
合约类型
将合约名称当作类型,可以传递合约实例。
OtherContract public other;
定长字节数组
bytes32 public hash;
bytes1 b = 0x12;
bytes1 到 bytes32,按位存储,有 .length 属性。
枚举
enum Status { Pending, Shipped, Delivered }
Status public current;
可与 uint 相互转换,默认值 0 对应第一个成员。
引用类型
引用类型需显式声明数据位置:storage(链上持久存储)、memory(函数内临时内存)、calldata(只读调用数据,节省 gas)。
数组
uint[] public fixedSize; // 动态数组
uint[5] memory arr; // 定长数组
- 支持
push、pop、length修改长度(仅storage态可变长数组)。 - 内存中数组需提前分配大小,
new uint[](3)。
结构体
struct Product {
uint id;
string name;
bool inStock;
}
Product public item;
提供构造函数式初始化:Product(1, "book", true) 或键值对 Product({id: 1, name: "book", inStock: true})。
映射(Mapping)
mapping(address => uint) public balances;
mapping(address => mapping(uint => bool)) public nested;
- 键类型允许值类型、
bytes、string、合约,不可为映射、数组、结构体。 - 仅
storage可用,不支持遍历,也不存在长度概念。
字符串与动态字节数组
string public name = "Alice";
bytes public data = "0xabcd";
string用于 UTF-8 文本,与bytes一样动态大小。string不支持直接通过索引获取字符,需转为bytes(s)。
修饰符
修饰符用于改变函数或变量的行为,包括可见性、状态可变性、自定义逻辑等。
可见性修饰符
函数可见性
public:内外均可调用。private:仅当前合约内可调用。internal:当前合约及继承合约可调用。external:仅从外部调用(内部调用需写this.f(),消耗 gas 更高)。
状态变量的可见性除 external 外均可使用。public 变量会自动生成同名 getter 函数。
uint public total; // 自动生成 total() 查询函数
uint private secret;
状态可变性修饰符
这些修饰符声明函数是否会读取或修改链上状态。
view:只读状态,不修改任何状态变量。pure:既不读取也不修改状态,仅依赖输入参数计算。- 无修饰符(默认):可读可写状态。
payable:允许函数接收以太币,没有该修饰符的函数调用时携带value会失败。
function getBalance() public view returns (uint) {
return address(this).balance;
}
function add(uint x, uint y) public pure returns (uint) {
return x + y;
}
function deposit() public payable {
// 可以接收 ETH
}
自定义修饰符
开发者可定义修饰符实现对前置条件、权限等的复用。
modifier onlyOwner {
require(msg.sender == owner, "Not owner");
_; // 继续执行函数体
}
function withdraw() public onlyOwner {
// 提款逻辑
}
_ 代表插入被修饰函数的主体。如需参数化:modifier fee(uint amount) { require(msg.value >= amount); _; }
其他重要修饰符与关键字
针对状态变量的修饰符
constant:用于编译期常量,不占用存储槽(如uint constant DAY = 24 hours;)。immutable:部署时赋值一次,之后不可变,存储方式更省 gas。
address public immutable creator;
constructor() { creator = msg.sender; }
继承相关修饰符
virtual:声明函数或函数修饰符可被子合约重写。override:子合约重写父合约函数时使用。
contract Base {
function foo() public virtual returns (string memory) {
return "base";
}
}
contract Child is Base {
function foo() public pure override returns (string memory) {
return "child";
}
}
重写多个父合约函数时写作 override(Base1, Base2)。
修饰符的执行顺序
自定义修饰符可组合使用,执行顺序为从右向左(或按声明顺序由外至内),_ 之前为前置,_ 之后为后置逻辑。
modifier A { _; }
modifier B { _; }
function f() A B { ... }
// 执行顺序:A的前置 → B的前置 → 函数体 → B的后置 → A的后置
综合示例
以下合约汇总基本结构、常见类型与修饰符用法:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Store {
address public immutable owner;
mapping(uint => Item) public items;
uint public itemCount;
struct Item {
string name;
uint price; // wei
bool available;
}
modifier onlyOwner() {
require(msg.sender == owner, "Unauthorized");
_;
}
constructor() {
owner = msg.sender;
}
function addItem(string calldata _name, uint _price) external onlyOwner {
items[itemCount] = Item(_name, _price, true);
itemCount++;
}
function buyItem(uint _id) external payable {
Item storage item = items[_id];
require(item.available, "Sold out");
require(msg.value >= item.price, "Insufficient payment");
item.available = false;
// 实际项目中转移资金给卖家等
}
function getItem(uint _id) external view returns (string memory, uint, bool) {
Item storage item = items[_id];
return (item.name, item.price, item.available);
}
}
该示例展示了:immutable 不可变变量、mapping 映射、struct 结构体、自定义修饰符 onlyOwner、external 与 view 函数、以及 storage 引用的使用。
通过掌握合约结构、Solidity 的类型划分以及各类修饰符的含义,你已经具备编写基本智能合约的能力。后续学习可深入异常处理、事件、库合约以及更高级的设计模式。