03-21-2026, 11:05 PM
[wiki系列] openmp/samp 关键字:初始值设定项
来自 SA-MP Wiki
目录
const
代码:
new const
MY_CONSTANT[] = {1, 2, 3};const 并不常用,但它用于声明一个无法被代码修改的变量。它的用途包括:让函数的数组参数编译得更高效,或者创建一个类似 #define 但却是数组的常量。const 是一个修饰符,必须与 new 或其他变量声明关键字搭配使用。如果你尝试修改 const 变量,编译器会报错。enum
枚举(enum)是一个非常实用的系统,用于表示大量数据并快速修改常量。主要用途有三类:
- 替代一大堆
#define语句
- 用符号名称表示数组下标(其实本质相同,只是写法不同)
- 创建新的标签(tag)
最常见的用法是定义数组结构:
代码:
enum E_MY_ARRAY
{
E_MY_ARRAY_MONEY,
E_MY_ARRAY_GUN
}
new
gPlayerData[MAX_PLAYERS][E_MY_ARRAY];
public OnPlayerConnect(playerid)
{
gPlayerData[playerid][E_MY_ARRAY_MONEY] = 0;
gPlayerData[playerid][E_MY_ARRAY_GUN] = 5;
}这会为每个玩家创建一个包含两个槽位的数组。连接时,
E_MY_ARRAY_MONEY 槽位设为 0,E_MY_ARRAY_GUN 槽位设为 5。如果不用 enum,代码会变成这样:
代码:
new
gPlayerData[MAX_PLAYERS][2];
public OnPlayerConnect(playerid)
{
gPlayerData[playerid][0] = 0;
gPlayerData[playerid][1] = 5;
}可读性明显变差——下标 0 和 1 分别代表什么?而且扩展性差:如果要在中间插入一个新槽位,就得手动把所有 1 改成 2,非常容易出错。而使用 enum,只需添加一行:
代码:
enum E_MY_ARRAY
{
E_MY_ARRAY_MONEY,
E_MY_ARRAY_AMMO,
E_MY_ARRAY_GUN
}
new
gPlayerData[MAX_PLAYERS][E_MY_ARRAY];
public OnPlayerConnect(playerid)
{
gPlayerData[playerid][E_MY_ARRAY_MONEY] = 0;
gPlayerData[playerid][E_MY_ARRAY_AMMO] = 100;
gPlayerData[playerid][E_MY_ARRAY_GUN] = 5;
}重新编译后,所有索引自动更新。
enum 的完整格式与工作原理
代码:
enum NAME (modifier)
{
NAME_ENTRY_1 = value,
NAME_ENTRY_2 = value,
...
NAME_ENTRY_N = value
}大部分内容是隐含的。默认 modifier 为
(+= 1),表示每个值都是前一个值 + 1:代码:
enum E_EXAMPLE
{
E_EXAMPLE_0,
E_EXAMPLE_1,
E_EXAMPLE_2
}结果:
E_EXAMPLE_0 = 0,E_EXAMPLE_1 = 1,E_EXAMPLE_2 = 2,E_EXAMPLE = 3(枚举名本身等于最后一个值)。修改 modifier 的例子:
代码:
enum E_EXAMPLE (+= 5)
{
E_EXAMPLE_0,
E_EXAMPLE_1,
E_EXAMPLE_2
}结果:
0, 5, 10, 15。如果声明 new gEnumArray[E_EXAMPLE];,数组大小为 15,但只能通过枚举名访问 0、5、10(仍可使用普通数字访问)。另一个例子(乘法):
代码:
enum E_EXAMPLE (*= 2)
{
E_EXAMPLE_0,
E_EXAMPLE_1,
E_EXAMPLE_2
}全部变为 0。解决方法是手动指定初始值:
代码:
enum E_EXAMPLE (*= 2)
{
E_EXAMPLE_0 = 1,
E_EXAMPLE_1,
E_EXAMPLE_2
}结果:
1, 2, 4, 8。提示:数组用途时强烈建议只用
+= 1,其他 modifier 容易导致混乱。enum 中可以包含数组
代码:
enum E_EXAMPLE
{
E_EXAMPLE_0[10],
E_EXAMPLE_1,
E_EXAMPLE_2
}结果:
E_EXAMPLE_0 = 0,E_EXAMPLE_1 = 10,E_EXAMPLE_2 = 11,E_EXAMPLE = 12(而不是很多人以为的 0、1、2、3)。enum 项可以带标签
代码:
enum E_MY_ARRAY
{
E_MY_ARRAY_MONEY,
E_MY_ARRAY_AMMO,
Float:E_MY_ARRAY_HEALTH,
E_MY_ARRAY_GUN
}
new
gPlayerData[MAX_PLAYERS][E_MY_ARRAY];
public OnPlayerConnect(playerid)
{
gPlayerData[playerid][E_MY_ARRAY_MONEY] = 0;
gPlayerData[playerid][E_MY_ARRAY_AMMO] = 100;
gPlayerData[playerid][E_MY_ARRAY_GUN] = 5;
gPlayerData[playerid][E_MY_ARRAY_HEALTH] = 50.0; // 不会产生标签不匹配警告
}enum 本身也可以作为标签使用(位标志)
代码:
enum E_MY_TAG (<<= 1)
{
E_MY_TAG_NONE,
E_MY_TAG_VAL_1 = 1,
E_MY_TAG_VAL_2,
E_MY_TAG_VAL_3,
E_MY_TAG_VAL_4
}
new
E_MY_TAG:gMyTagVar = E_MY_TAG_VAL_2 | E_MY_TAG_VAL_3;gMyTagVar 值为 6(4 | 2),并带有自定义标签。直接赋值 gMyTagVar = 7; 会产生标签不匹配警告,但可以用强制转换绕过:代码:
gMyTagVar = E_MY_TAG:7;进阶用法(掩码 + 组合):
代码:
enum E_MY_TAG (<<= 1)
{
E_MY_TAG_NONE,
E_MY_TAG_MASK = 0xFF,
E_MY_TAG_VAL_1 = 0x100,
E_MY_TAG_VAL_2,
E_MY_TAG_VAL_3,
E_MY_TAG_VAL_4
}
new
E_MY_TAG:gMyTagVar = E_MY_TAG_VAL_2 | E_MY_TAG_VAL_3 | (E_MY_TAG:7 & E_MY_TAG_MASK);结果为 1543(0x0607)。
用无名 enum 替代 #define
传统写法:
代码:
#define TEAM_NONE 0
#define TEAM_COP 1
#define TEAM_ROBBER 2
#define TEAM_CIV 3
#define TEAM_CLERK 4
#define TEAM_DRIVER 5改用 enum 自动赋值:
代码:
enum
{
TEAM_NONE,
TEAM_COP,
TEAM_ROBBER,
TEAM_CIV,
TEAM_CLERK,
TEAM_DRIVER
}值完全相同,使用方式也一样。
更强大的位标志写法(推荐):
代码:
enum (<<= 1)
{
TEAM_NONE,
TEAM_COP = 1,
TEAM_ROBBER,
TEAM_CIV,
TEAM_CLERK,
TEAM_DRIVER,
TEAM_ADMIN
}现在
TEAM_COP = 1(二进制 00000001)、TEAM_ROBBER = 2(00000010)等。一个变量就能同时表示多个团队:- 添加团队:
gPlayerTeam[playerid] |= TEAM_COP;
- 移除团队:
gPlayerTeam[playerid] &= ~TEAM_COP;
- 检查是否在团队:
if (gPlayerTeam[playerid] & TEAM_COP)
非常简洁且强大。
forward
forward 用于告诉编译器“这个函数稍后会定义”。所有 public 函数都必须 forward,也可以用于其他地方。语法:
代码:
forward MyPublicFunction(playerid, const string[]);代码:
forward MyPublicFunction(playerid, const string[]);
public MyPublicFunction(playerid, const string[])
{
}除了 public 函数外,
forward 还能解决一种罕见的“reparse”警告(当函数返回带标签的值如 Float 时,在声明前被调用):有警告的写法:
代码:
main()
{
new Float:myVar = MyFloatFunction();
}
Float:MyFloatFunction()
{
return 5.0;
}解决方法 1(函数前置):
代码:
Float:MyFloatFunction()
{
return 5.0;
}
main()
{
new Float:myVar = MyFloatFunction();
}解决方法 2(forward):
代码:
forward Float:MyFloatFunction();
main()
{
new Float:myVar = MyFloatFunction();
}
Float:MyFloatFunction()
{
return 5.0;
}注意:forward 也要带返回值标签。
native
native 函数是由虚拟机(openmp(samp) 服务器或插件)提供的函数,而不是脚本自己定义的。你只能声明已存在于 openmp(samp) 或插件中的 native。用途 1:让自定义函数出现在 qawno 右侧函数列表中(即使是假的):
代码:
/*
native MyFunction(playerid);
*/注释会被 qawno 识别加入列表,但编译器会忽略。
用途 2:重命名 / 重载函数:
代码:
native my_print(const string[]) = print;现在
print 函数在脚本中已不存在(但服务器内部仍存在)。你可以重新定义它:代码:
print(const string[])
{
my_print("Someone called print()");
my_print(string);
}以后所有
print() 调用都会先执行你的代码,再调用原函数。new
new 是变量声明的核心关键字之一。代码:
new
myVar = 5;创建变量并赋值为 5。未赋值时默认为 0:
代码:
new myVar;
printf("%d", myVar); // 输出 0作用域(scope)由大括号
{} 决定,声明在括号内的变量只能在该括号内使用。代码:
if (a == 1)
{
new myVar = 5;
printf("%d", myVar); // 可以
if (myVar == 1)
{
printf("%d", myVar); // 可以
}
}
// 这里无法使用 myVar,会报错
printf("%d", myVar);全局变量(在函数外声明)从声明位置之后可在整个脚本中使用(包括
#include 的其他文件)。static 与 new 的区别详见下方
static 部分。operator
允许为自定义标签重载运算符。
示例(大端序转换):
代码:
stock BigEndian:operator=(b)
{
return BigEndian:(((b >>> 24) & 0x000000FF) | ((b >>> 8) & 0x0000FF00) | ((b << 8) & 0x00FF0000) | ((b << 24) & 0xFF000000));
}
main()
{
new BigEndian:a = 7;
printf("%d", _:a); // 输出 117440512(因为小端序读取大端序数据)
}可重载的运算符:
+, -, *, /, %, ++, --, ==, !=, <, >, <=, >=, !, =你可以让运算符做任何事(不一定是原本功能):
代码:
stock BigEndian:operator+(BigEndian:a, BigEndian:b)
{
return BigEndian:42;
}a + b 永远返回 42。public
public 让函数对虚拟机可见,允许 openmp(samp) 服务器从外部直接调用(而非仅在脚本内部调用)。也可用于变量(允许服务器读写),但 openmp(samp) 中极少使用。public 函数的名称会存储在 .amx 文件中(普通函数只存地址),这也是反编译的难点之一。通过名称调用 public 函数:
代码:
forward MyPublicFunc();
main()
{
CallLocalFunction("MyPublicFunc", "");
}
public MyPublicFunc()
{
printf("Hello");
}也可使用
@ 前缀:代码:
forward MyPublicFunc();
forward @MyOtherPublicFunc(var);
main()
{
CallLocalFunction("MyPublicFunc", "");
SetTimerEx("@MyOtherPublicFunc", 5000, 0, "i", 7);
}
public MyPublicFunc()
{
printf("Hello");
}
@MyOtherPublicFunc(var)
{
printf("%d", var);
}所有 openmp(samp) 回调(如
OnPlayerConnect)都是 public,由服务器自动调用。注意:
public 函数也可以像普通函数一样直接调用(速度更快):代码:
forward MyPublicFunc();
main()
{
MyPublicFunc(); // 直接调用,比 CallLocalFunction 快
}
public MyPublicFunc()
{
printf("Hello");
}static
static 全局变量类似 new,但作用域更小(仅限于声明所在的文件或 #section)。对比
new 的跨文件可见性,static 仅限本文件:代码:
// file2.pwn 中无法访问 file1.pwn 里的 static 变量局部 static:作用域与
new 相同(仅大括号内),但值在函数多次调用间保留(不像 new 每次重置)。代码:
MyFunc()
{
new i = 0;
printf("%d", i);
i++;
printf("%d", i);
}调用 4 次输出:
代码:
0 1
0 1
0 1
0 1改为
static 后:代码:
0 1
1 2
2 3
3 4初始化值仅在第一次调用时生效。
static 函数只能在声明所在文件中调用,适合“私有”函数。stock
stock 用于声明可能不会被使用的变量或函数,避免产生“unused”警告。代码:
new stock
gMayBeUsedVar;
static stock
g_sMayBeUsedVar;如果被使用则编译保留;如果未使用则完全剔除(不像
#pragma unused 只隐藏警告)。最常见用途:编写库时。库作者无法预知用户会用哪些函数,使用
stock 可避免大量无用警告。```pawn
stock Func1()
{
printf("Hello");
}
stock Func2()
{
printf("Hi");
}
#GTA# #圣安地列斯# #侠盗猎车手# #圣安地列斯联机# #samp# #gta联机# #gtasa联机# #openmp# #omp# #open.mp# #gtasa#
社区交流群: 673335567
论坛: https://open-mp.cn/

