03-21-2026, 11:06 PM
[wiki系列] openmp/samp 关键字:指令
来自 SA-MP Wiki
目录
- 1 #assert
- 2 #define
- 3 #else
- 4 #elseif
- 5 #emit
- 6 #endif
- 7 #endinput , #endscript
- 8 #error
- 9 #if
- 10 #include
- 11 #pragma
- 11.1 说明
- 11.2 列表
- 11.3 示例
- 11.3.1 已弃用(Deprecated)
- 12 #tryinclude
- 13 #undef
#assert
此指令会检查常量表达式是否为真,如果不为真则停止编译。
代码:
#define MOO 10
#assert MOO > 5以上代码可以正常编译。
代码:
#define MOO 1
#assert MOO > 5以上代码无法编译,会产生致命错误。
这类似于:
代码:
#define MOO 1
#if MOO <= 5
#error Moo check failed
#endif但是
#assert 会给出以下错误信息:代码:
"Assertation failed: 1 > 5"而第二种写法会给出:
代码:
"User error: Moo check failed"哪种错误信息更实用,则视情况而定。
#define
#define 是一个文本替换指令。只要在代码中找到被定义的符号(宏名),就会将其替换为定义的内容。代码:
#define MOO 7
printf("%d", MOO);会被替换为:
代码:
printf("%d", 7);这也是为什么反编译后的代码中看不到任何
#define,因为所有指令都在预处理器阶段处理完毕。定义的内容不一定是数字:代码:
#define PL new i = 0; i < MAX_PLAYERS; i++) if (IsPlayerConnected(i)
for (PL)
{
printf("%d connected", i);
}会编译成大家熟悉(或讨厌)的玩家循环。注意括号的使用:一部分来自
for 语句,一部分来自宏本身。另一个鲜为人知的特性是:定义可以跨行(通过在行尾加上反斜杠
\ 来转义换行)。一般来说换行会结束定义,但下面这样是有效的:代码:
#define PL \
new i = 0; i < MAX_PLAYERS; i++) \
if (IsPlayerConnected(i)
for (PL)
{
printf("%d connected", i);
}\ 字符表示定义继续到下一行。#define 的第三个重要特性是支持参数(宏参数)。参数用 %0 到 %9 表示,用法与普通函数参数类似:代码:
#define MOO(%0) \
((%0) * 7)
printf("%d", MOO(6));输出结果为 42(不是随意选的)。注意宏里多余的括号——这是因为宏是纯文本替换,所以实际编译成:
代码:
printf("%d", ((6) * 7));这样是安全的。但看下面这个例子:
代码:
printf("%d", MOO(5 + 6));你会期望结果是 77((5 + 6) * 7)。加上括号后确实如此;如果去掉括号:
代码:
#define MOO(%0) \
%0 * 7
printf("%d", MOO(5 + 6));实际会变成:
代码:
printf("%d", 5 + 6 * 7);由于运算优先级(BODMAS),结果变为 47(5 + (6 * 7)),这就完全错误了。
关于参数还有一个有趣的事实:如果传入的参数数量超过定义的数量,最后一个参数会包含所有多余的部分。例如:
代码:
#define PP(%0,%1) \
printf(%0, %1)
PP("%s %s %s", "hi", "hello", "hi");实际会打印:
代码:
hi hello hi因为
%1 包含了 "hi", "hello", "hi"。你可能还注意到 # 可以把字面量转换成字符串,这是 openmp(samp) 特有的功能,这里只是为了区分参数。#else
#else 相当于普通 else,但用于 #if 条件。#elseif
#elseif 相当于 else if,但用于 #if 条件。代码:
#define MOO 10
#if MOO == 9
printf("if");
#elseif MOO == 8
printf("else if");
#else
printf("else");
#endif#emit
此指令在 pawn-lang.pdf 的表格中并未列出,但实际存在。它相当于内联汇编器,如果你熟悉 AMX 字节码,可以用它直接插入 AMX 操作码。唯一限制是每次只能带一个参数。
语法:
#emit <opcode> <argument><argument> 可以是小数、整数或(局部/全局)符号(变量、函数、标签)。操作码列表及其含义可在 Pawn Toolkit ver. 3664 中找到。
#endif
#endif 相当于 #if 的结束括号。#if 不使用大括号,所有条件内容一直持续到对应的 #endif。#endinput , #endscript
此指令停止当前文件的包含(不再继续读取该文件)。
#error
此指令立即停止编译,并输出自定义错误信息。示例见
#assert。#if
#if 是预处理器版本的 if。你可以精确控制哪些代码被编译、哪些不被编译。例如:代码:
#define LIMIT 10
if (LIMIT < 10)
{
printf("Limit too low");
}会编译成:
代码:
if (10 < 10)
{
printf("Limit too low");
}编译器知道这个条件永远为假,因此会给出“常量表达式”警告。但既然永远不会成立,为什么还要保留这段代码呢?你可以直接删掉,但之后别人修改
LIMIT 重新编译时就无法检查了。这就是 #if 的作用。普通
if 在常量表达式时会警告,而 #if 必须 是常量表达式:代码:
#define LIMIT 10
#if LIMIT < 10
#error Limit too low
#endif这样会在编译时就检查限制是否过小,如果是则直接报错,而不用运行脚本测试。同时也不会生成多余的代码。注意
#if 不强制使用括号(虽然复杂表达式可能需要)。另一个例子:
代码:
#define LIMIT 10
if (LIMIT < 10)
{
printf("Limit less than 10");
}
else
{
printf("Limit equal to or above 10");
}同样是常量检查,会产生警告,而且两个
printf 都会被编译(尽管我们知道只会执行一个)。用 #if 改写后:代码:
#define LIMIT 10
#if LIMIT < 10
printf("Limit less than 10");
#else
printf("Limit equal to or above 10");
#endif这样只有需要的
printf 会被编译,另一个仍保留在源代码中(方便以后改 LIMIT),但不会占用最终编译后的代码空间,也不会每次运行都执行无用的判断。#include
此指令会把指定文件的所有代码插入到
#include 所在的位置。有两种包含方式:相对路径(用双引号)和系统路径(用尖括号,我自己起的名称,如果你有更好的叫法请告诉我)。相对包含使用双引号,路径相对于当前文件:
代码:
#include "me.pwn"会包含与当前文件同目录下的
me.pwn。系统包含使用尖括号,从 Pawn 编译器所在目录下的
include 文件夹(或其父目录的 include 文件夹)中查找:代码:
#include <me>会包含
qawno/include/me.inc(注意没有扩展名;如果文件不是 .p 或 .inc,可以指定扩展名)。两种方式都支持子目录:
代码:
#include "folder/me.pwn"
#include <folder/me>如果文件不存在,编译会立即失败。
#pragma
说明
这是最复杂的指令之一。它提供了一系列选项来控制脚本的编译行为。例如:
代码:
#pragma ctrlchar '$'会把转义字符从
\ 改成 $,所以换行符不再是 "\r\n",而是 "$r$n"。很多选项原本是为嵌入式系统设计的,用来限制 PC 上几乎无限制的资源。这里只列出与 openmp(samp) 相关的选项(完整列表见 pawn-lang.pdf)。
列表
| 名称 | 值 | 说明 |
|---|---|---|
| codepage | 名称/值 | 设置字符串使用的 Unicode 码页 |
| compress | 1/0 | openmp(samp) 不支持,请勿使用 |
| deprecated | 符号名称 | 如果使用该符号会产生警告,用于提示有更好的替代版本 |
| dynamic | 值(通常是 2 的幂) | 设置栈和堆内存大小(单位:cells)。出现“excessive memory usage”警告时需要设置 |
| library | dll 名称 | 在 openmp(samp) 中经常被误用。它指定本文件中的原生函数来自哪个 DLL,并非把文件定义为库 |
| pack | 1/0 | 交换 !"" 和 "" 的含义(详见 pawn-lang.pdf 中的打包字符串说明) |
| tabsize | 值 | 经常被误用。用于设置制表符宽度,避免因空格和制表符混用产生的警告。openmp(samp) 默认设为 4(qawno 编辑器中制表符宽度)。设为 0 会关闭所有缩进警告,但强烈不推荐(会导致代码难以阅读) |
| unused | 符号名称 | 类似 deprecated,用于抑制“symbol is never used”警告。推荐使用 stock,但某些情况(如函数参数)无法使用 stock 时才用此方式 |
示例
已弃用(Deprecated)
代码:
new
gOldVariable = 5;
#pragma deprecated gOldVariable
main()
{
printf("%d", gOldVariable);
}使用
gOldVariable 时会产生警告,提示不应再使用它。主要用于函数更新 API 时保留向下兼容性。#tryinclude
类似于
#include,但如果文件不存在,编译不会失败。这非常适合根据用户是否安装了特定插件(或插件的 include 文件)来选择性包含功能。myinc.inc:
代码:
#if defined _MY_INC_INC
#endinput
#endif
#define _MY_INC_INC
stock MyIncFunc()
{
printf("Hello");
}主脚本:
代码:
#tryinclude <myinc>
main()
{
#if defined _MY_INC_INC
MyIncFunc();
#endif
}只有当
myinc.inc 存在并被成功包含时,才会调用 MyIncFunc()。这对 IRC 插件等需要检测插件是否安装的情况非常有用。#undef
移除之前定义的宏或常量符号。
代码:
#define MOO 10
printf("%d", MOO);
#undef MOO
printf("%d", MOO);第二个
printf 会编译失败,因为 MOO 已被取消定义。代码:
enum
{
e_example = 300
};
printf("%d", e_example);
#undef e_example
printf("%d", e_example); // 致命错误#GTA# #圣安地列斯# #侠盗猎车手# #圣安地列斯联机# #samp# #gta联机# #gtasa联机# #openmp# #omp# #open.mp# #gtasa#
社区交流群: 673335567
论坛: https://open-mp.cn/

