跳到主要内容

脚本编写: 标签

引言

标签(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毫无相似性)。