samp | open.mp 联机社区论坛
[wiki系列] openmp/samp 关键字:初始值设定项 - 打印版本

+- samp | open.mp 联机社区论坛 (https://open-mp.cn)
+-- 板块: SA-MP (https://open-mp.cn/forumdisplay.php?fid=12)
+--- 板块: 教程 (https://open-mp.cn/forumdisplay.php?fid=17)
+--- 主题: [wiki系列] openmp/samp 关键字:初始值设定项 (/showthread.php?tid=21)



[wiki系列] openmp/samp 关键字:初始值设定项 - 小鸟unsigned - 03-21-2026

[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 = 0E_EXAMPLE_1 = 1E_EXAMPLE_2 = 2E_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 = 0E_EXAMPLE_1 = 10E_EXAMPLE_2 = 11E_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 的其他文件)。

staticnew 的区别详见下方 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/