跳到主要内容

Pawn 代码风格指南

本文档是关于 Pawn 源码通用命名规范及其他代码风格要素的简明指南,旨在提升代码意图传达效率,并优化调试与共享流程。

扩展阅读:

术语定义

语句(Statement)

语句是命令式指示宿主程序执行操作的代码单元,由可产生结果的合法代码构成。

a = b + c;

这是由变量赋值操作组成的语句,其右侧使用了[#表达式]。

SetPlayerColor(playerid, 0xFF4700FF);

这是调用函数并传递参数的语句。

x + 8

这并非语句,因其结果未被使用,仅构成一个[#表达式]。

复合语句(Compound Statement)

由大括号包裹的多个语句集合。

{
new message[] = "hi!";
print(message);
}

包含两个语句的复合语句。

if (a == b)
{
print("hello!");
}

带有if条件的复合语句,通常称为"if 语句"。

return Function1(), Function2(), Function3();

这并非复合语句,而是通过逗号分隔的语句链。此类写法属于不良实践。

表达式(Expression)

可产生值的语法单元,除非该值被使用,否则不能构成有效语句。

表达式通常组合形成语句。

a + b

简单的加法表达式,对两个值执行加法运算。

代码规范

大括号格式

推荐使用 Allman 风格大括号:

function()
{
//
}

若已形成肌肉记忆,亦可采用 K&R 风格:

function() {
//
}

条件分支

switch语句需使用两级缩进:一级用于switch代码块,另一级用于每个case子句或复合语句。

switch (variable)
{
case 0:
return 0;
case 1:
return 1;
case 2:
return 2;
default:
return -1;
}
switch (variable)
{
case 0:
{
// 代码...
return 0;
}
case 1:
{
// 代码...
return 1;
}
case 2:
{
// 代码...
return 2;
}
default:
{
// 代码...
return -1;
}
}

复合语句(代码块)

代码块必须始终使用大括号包裹,即使仅包含单行代码。此规则适用于所有层级,包括函数定义。

func()
{
singleLineExpr();
}
func()
{
if ()
{
singleLineExpr();
}
}
func()
{
if ()
{
singleLineExpr();
}
else if ()
{
//
}
else
{
//
}
}

命名规范

函数命名

函数必须采用PascalCase大驼峰命名法。

全局变量

  • 通过new声明的全局变量必须始终使用g_前缀的大驼峰命名法,如g_VariableName
  • 通过static声明的全局变量必须始终使用s_前缀的大驼峰命名法,如s_VariableName
  • 常量全局变量必须使用全大写蛇形命名法(SCREAMING_SNAKE_CASE)

局部变量

局部变量必须使用camelCase小驼峰命名法,且禁止使用单字母命名,以下情况除外:

  • for循环中的i, j, k等迭代变量
  • 数学上下文中使用的x, y, z等坐标变量

枚举类型

具名枚举类型必须使用E_前缀(强标签)或e_前缀(弱标签):

枚举字段必须同样采用全大写蛇形命名法(SCREAMING_SNAKE_CASE),并以枚举器名称作为前缀。例如:

static enum E_PLAYER_DATA {
E_PLAYER_CASH,
Float:E_PLAYER_HEALTH,
}

弱标签示例:

static enum e_PLAYER_DATA {
E_PLAYER_CASH,
Float:E_PLAYER_HEALTH,
}

匿名枚举字段必须同样使用全大写蛇形命名法,并以枚举类型名称为前缀:

static enum {
ENUMATOR_INTEGER,
Float:ENUMATOR_FLOAT,
}

除非需跨模块使用,否则枚举必须始终声明为static

宏与预处理定义

  • 宏定义必须始终使用全大写蛇形命名法(SCREAMING_SNAKE_CASE
  • 预处理常量定义同样使用全大写蛇形命名法

此规范有助于区分变量/常量、函数/宏的关系。

建议避免创造新语法元素,以防止初学者混淆语言特性与库函数。但部分历史遗留库因兼容性要求可能例外。

文档规范

导出函数必须使用行注释进行文档说明,格式为// FunctionName 实现X、Y、Z功能并返回A,首词为函数名,后接功能简述。无需详细描述每个参数,例如:

// LoadPlayerAccount 用于初始化账户加载流程。本函数将触发HTTP请求
// 获取玩家数据,向玩家显示对话框,最终在流程完成后触发`OnPlayerLogin`
// 事件。若加载失败则踢出玩家。
stock Error:LoadPlayerAccount(playerid)
{
// 代码...
}

每个代码包应包含README文档,必要时在模块首行添加功能描述注释。