脚本编写: 标签
引言
标签(Tag)是变量的前缀,用于告知编译器在特定情况下特殊处理该变量。例如您可以通过标签定义变量的使用范围,或者指定变量的特殊运算方式。
标签分为两种类型:
- 强标签(以大写字母开头)
- 弱标签(以小写字母开头)
两者的主要区别在于:弱标签在某些情况下可能被编译器静默转换为无标签变量(即不会产生警告),而强标签在隐式转换时总会触发警告。
以下是一个简单示例:
new
File:myfile = fopen("file.txt", io_read);
myFile += 4;
fopen
函数返回带有File:
标签的值,首行赋值操作没有问题(注意标签大小写需一致)。但第二行对文件句柄进行数值加法运算时,4
是无标签值,而myFile
带有File:
标签,这种类型不匹配将触发编译器警告。这种机制非常有用,因为文件句柄的实际数值没有意义,修改它会导致句柄失效从而无法关闭文件。
强标签
强标签以大写字母开头,SA:MP 中的常见示例包括:
Float:
File:
Text:
强标签变量不可与其他类型混用,以下操作都会触发警告:
new
Float:myFloat,
File:myFile,
myBlank;
myFile = fopen("file.txt", io_read); // File: = File:,无警告
myFloat = myFile; // Float: = File:,"标签不匹配"警告
myFloat = 4; // Float: = _:(无标签),"标签不匹配"警告
myBlank = myFloat; // _:(无标签) = Float:,"标签不匹配"警告
弱标签
弱标签的行为与强标签基本相似,但当目标变量无标签时,弱标签转换不会触发警告:
new
Strong:myStrong,
weak:myWeak,
myNone;
myNone = myStrong; // 触发警告
myNone = myWeak; // 无警告
但反向操作仍会触发警告:
myWeak = myNone; // 触发警告
函数参数传递同理:向无标签参数传递弱标签变量不会触发警告:
new
weak:myWeak;
MyFunction(myWeak);
MyFunction(myVar)
{
...
}
但向带标签参数(无论强弱)传递无标签参数会触发警告。SA:MP 中弱标签的典型示例包括:
bool:
filemode:
floatround_method:
使用方法
声明变量
声明带标签变量非常简单,直接书写标签即可(无需预先定义标签):
new
Mytag:myVariable;
使用现有标签声明变量后,即可将该变量用于该标签对应的函数和运算符。
函数定义
定义带标签参数的函数时,只需在参数或返回值前添加标签:
Float:GetValue(File:fHnd, const name[])
{
...
}
该函数接收文件句柄并返回浮点数值(假设是从文件中读取并与name[]
参数传递的数值名称对应的值)。此函数很可能使用floatstr
函数(通过在 Pawno 右侧函数列表点击该函数时查看状态栏可知,该函数也返回Float:
类型值)进行字符串转换。虽然具体实现细节无关紧要,但其核心逻辑是将字符串转换为 IEEE 浮点数值,并以 PAWN 单元(cell)形式存储(实际上严格存储为整型数值,只是其位模式恰好与对应的 IEEE 浮点数一致——这正是标签机制部分要解决的问题,因为 PAWN 本身是无类型语言)。
运算符重载
运算符(例如 +
、==
、>
等)可为不同标签实现重载。这意味着对两个Float:
变量执行+
运算,其行为将与无标签变量的加法运算截然不同。该特性对浮点型变量尤为重要——如先前所述,Float:
变量并非真正的浮点数,而是采用特定位模式的整型存储。若未重载运算符,这些运算将在整型层面直接执行,若将结果重新解释为浮点数将产生无效结果。因此Float:
标签重载了多数运算符,通过调用服务端的特殊数学函数实现运算(而非在 PAWN 脚本层处理)。
运算符重载函数的定义方式与普通函数类似,区别在于需使用"operator(符号)"语法代替函数名,其中(符号)代表被重载的运算符。可重载的合法运算符包括:
+
-
=
++
--
==
*
/
!=
>
<
>=
<=
!
%
诸如\
、*
、=
等运算符会被自动处理。而&
等位运算符无法被重载。您还可以针对不同的标签组合多次重载同一个运算符。例如:
stock Float:operator=(Mytag:oper)
{
return float(_:oper);
}
添加此代码后,以下操作将不再触发警告:
new
Float:myFloat,
Mytag:myTag;
myFloat = myTag;
由于Float: = Mytag:
这种情况下的赋值运算符=
已被显式处理,编译器现在能够准确理解其操作逻辑,因此您将不再收到此前会出现的编译器警告。
标签覆盖
使用_:
前缀可强制忽略变量标签。但需谨慎处理类型转换:
return float(_:oper);
这是标签覆盖的典型示例。操作数前的_:
表示编译器将忽略oper
变量原有的Mytag:
标签类型,强制将其视为_:
标签类型(即无标签类型)。float()
函数用于将普通数值标记为浮点类型,因此必须接收无标签数值。在此示例中,我们假设Mytag
存储的是普通整型数值,但进行标签覆盖时必须格外谨慎。例如下列操作将产生异常结果:
new
Float:f1,
Float:f2 = 4.0;
f1 = float(_:f2);
根据常识推断,f1
应被赋值为4.0
,但实际结果并非如此。如前所述,f2
变量存储的是4.0
的 IEEE 浮点位模式表示(而非简单的整型数值4
),这意味着当您要求编译器将该变量作为整型处理时,它会直接将变量中的位模式作为整型值读取(而不会执行浮点到整型的数值转换)。因此您将得到一个看似随机的数字(实际上该数值遵循 IEEE 浮点规范的结构规律,但其整型表现形式与4.0
毫无相似性)。