cs1.6 amxx编程 enum精研教程 -- 作者:偶萤蛉(Oinling)

目录:

1:enum声明枚举常量的语法格式

2:enum表达式详解

2.1:enum

2.2:枚举标签

2.3:枚举名称

2.4:增值法

2.5:枚举体入口

2.6:元素标签

2.7:枚举成员名称

2.8:元素尺寸

2.9:初始化表达式

2.10: , 符号

2.11:枚举体出口

2.12: ; 符号

3:枚举实例

3.1:枚举标签

3.2:枚举名称

3.3:增值法

3.4:元素标签

3.5:枚举成员名称

3.6:元素尺寸

3.7:初始化表达式

1:enum声明枚举常量的语法格式

enum 枚举标签 枚举名称 增值法 枚举体入口 元素标签 枚举成员名称1 元素尺寸 初始化表达式, 元素标签 枚举成员名称2 元素尺寸 初始化表达式, 元素标签 枚举成员名称3 元素尺寸 初始化表达式, 枚举体出口;

参考:

enum CsTeams: Example (+= 2) { any: Eg1 [3] = 0, bool: Eg2 [3] = 2, Float: Eg3 [3] = 2 + 2, };

2:enum表达式详解

除了enum和枚举体出入口,其它都是可省略的.也就是说,可以没有枚举成员.也可以设定多个枚举成员.

整个表达式中,所有运算表达式都仅支持编译时常量表达式.也就是说,填写运行时函数是不允许的.

包括浮点数的运算,同样是运行时执行,因此不可填写浮点数的运算表达式.

枚举通常用于提高代码的可读性,可维护性和安全性.

提高可读性:使用有意义的符号名称,代替字面量,可以让代码更易读和理解.

提高可维护性:如果需要修改常量值,只需要修改声明位置的初始化表达式,不需要在代码多处位置进行替换.

提高安全性:枚举可以限制只能从预定义值中选择,防止赋予无效的值.

枚举还有一些高级功能.

熟练使用枚举,可令AMXX插件拥有类似结构体,自定义类型,重载等一些面向对象的能力.

能帮助开发者提高代码的可维护性,方便地重复运用或扩展现有的代码.

能更直观地模拟实际世界中的实体及其关系,使编程思维贴近问题.

2.1:enum

enum可在函数体内使用,也可在函数体外使用.一般而言,enum用于声明全局常量.

2.2:枚举标签

枚举标签用于设定常量拥有的标签,若省略,则以枚举名称作为标签.

2.3:枚举名称

枚举名称是可自定义名称的常量标识符.参照标识符的命名规则.

若同时省略枚举标签与枚举名称,则以_:作为常量拥有的标签.

2.4:增值法

增值法必须要有一对圆括号.其中可选则+= *= <<=三种运算符,表示每个成员与上一个成员的增值关系.

运算符的右值仅支持常量表达式.比如 (+= -5) (<<= 1) (*= 1 + 1) 等等.

省略增值法则默认使用(+= 1)增值法.表示每个成员等于上一成员的值+1.

2.5:枚举体入口

枚举体入口,即代码块入口,用 { 符号表示.

枚举体内,只能声明枚举成员,不能运行函数,不能使用任意保留词.但允许访问其它无地址常量.

2.6:元素标签

元素标签表示将该成员作为数组索引使用时,访问的元素拥有什么标签.若省略,则使用数组自身的标签.

2.7:枚举成员名称

枚举成员是可自定义名称的常量标识符.参照标识符的命名规则.

枚举成员名称是可以与其它无地址常量标识符重名的.并且不同版本表现不同,在AMXX 1.9.0版本,开发者只能访问第一个声明的同名常量.

由于不会触发警告或报错,开发者应小心这一点,不要使用相同的命名.

2.8:元素尺寸

元素尺寸用一对方括号包裹一个整数字面量表示.

枚举成员被用作数组索引时,元素尺寸用于告知编译器,以枚举成员的值为起点,将多少个连续元素视作整体.

这个整体,将被视作独立的数组(或字符串)元素,访问这个数组时,与其它数组一样.

若省略元素尺寸,则只能访问1个元素.

2.9:初始化表达式

初始化表达式仅支持常量表达式,表示该枚举成员储存的字面量是什么.

若省略初始化表达式,则由增值法决定枚举成员储存的值.

2.10: , 符号

若要连续声明多个枚举成员,用 , 符号隔开它们.

最后一个枚举成员可以省略结尾的 , 符号.但是保留也不会导致编译器警告或报错.

2.11:枚举体出口

枚举体出口,即代码块出口.用 } 符号表示.

最终,由增值法决定枚举名称储存的值.若省略增值法,则默认枚举名称等于最后一个成员的值+1.

从枚举体出口开始,接下来代码中出现的枚举名称和枚举成员,都会在编译期间,被替换为它们储存的值.

因此,运行期间不需要通过查询地址访问数值,比起有地址的变量或常量,这种无地址常量有更高的效率.

2.12: ; 符号

枚举体出口允许跟随一个 ; 符号,编译时会被编译器吃掉.而且检查分号模式下,不检查是否有分号.

3:枚举实例

3.1:枚举标签

在以下代码中,由于设定了枚举标签为Float:,因此ExampleEg1所储存的值带有Float:标签.

float函数要求参数1带有_:标签,因此会触发Tag mismatch警告(标签不匹配).

floatround函数要求参数1带有Float:标签,因此不会触发Tag mismatch警告(标签不匹配).

#include amxmodx public plugin_init() { enum Float:Example { Eg1 } float(Example); float(Eg1); floatround(Example); floatround(Eg1); }

填写了枚举标签后,编译器不会创建一个与枚举名称同名的标签.因此枚举名称失去标签的作用.

实际上,枚举时一般不填写枚举标签.

3.2:枚举名称

枚举名称与宏定义相似,声明后便可以被引用.

编译时,源码中所有引用被删除,替换为其储存的字面量.

因此,枚举名称是一种无地址常量,它的值无法被更改.

在编译时就能确定无地址常量的值,运行时不需要加载标识符,因此效率更高.

以下代码用于展示各种变量,常量声明在效率上的差异.可看到单纯用constenum声明的无地址常量消耗时间更少:

#include amxmodx public plugin_init() { // 循环次数 const numberOfCycles = 10000000; LoopTest("new", numberOfCycles); LoopTest("new public", numberOfCycles); LoopTest("new public stock", numberOfCycles); LoopTest("new public stock const", numberOfCycles); LoopTest("new static", numberOfCycles); LoopTest("new static stock", numberOfCycles); LoopTest("new static stock const", numberOfCycles); LoopTest("new stock", numberOfCycles); LoopTest("new stock const", numberOfCycles); LoopTest("new const", numberOfCycles); LoopTest("public", numberOfCycles); LoopTest("public stock", numberOfCycles); LoopTest("public stock const", numberOfCycles); LoopTest("static", numberOfCycles); LoopTest("static stock", numberOfCycles); LoopTest("static stock const", numberOfCycles); LoopTest("stock", numberOfCycles); LoopTest("stock const", numberOfCycles); LoopTest("const", numberOfCycles); LoopTest("enum", numberOfCycles); LoopTest("enum member", numberOfCycles); LoopTest("local const", numberOfCycles); LoopTest("local enum", numberOfCycles); LoopTest("local enum member", numberOfCycles); LoopTest("local new", numberOfCycles); LoopTest("local new const", numberOfCycles); LoopTest("local static", numberOfCycles); LoopTest("local static const", numberOfCycles); } new v1; new public v2; new public stock v3; new public stock const v4; new static v5; new static stock v6; new static stock const v7; new stock v8; new stock const v9; new const v10; public v11; public stock v12; public stock const v13; static v14; static stock v15; static stock const v16; stock v17; stock const v18; const v19 = 0; // 无地址常量 enum _: v20 (+=0) { v21 } // 无地址常量 static stock LoopTest(const specifier[], numberOfCycles) { #define g循环访问全局符号(%1,%2)\ if (equal(specifier, %1))\ {\ counter = numberOfCycles;\ timer = tickcount();\ while (counter--) counter +=\ %2 + %2 + %2 + %2 + %2 + %2 + %2 + %2 + %2 + %2 +\ %2 + %2 + %2 + %2 + %2 + %2 + %2 + %2 + %2 + %2 +\ %2 + %2 + %2 + %2 + %2 + %2 + %2 + %2 + %2 + %2 +\ %2 + %2 + %2 + %2 + %2 + %2 + %2 + %2 + %2 + %2 +\ %2 + %2 + %2 + %2 + %2 + %2 + %2 + %2 + %2 + %2;\ timer = tickcount() - timer;\ server_print("[AMXX]访问%024s符号%d*50次,消耗%d毫秒", specifier, numberOfCycles, timer);\ return;\ } #define l循环访问局部符号(%1,%2,%3)\ if (equal(specifier, %1))\ {\ %2;\ counter = numberOfCycles;\ timer = tickcount();\ while (counter--) counter +=\ %3 + %3 + %3 + %3 + %3 + %3 + %3 + %3 + %3 + %3 +\ %3 + %3 + %3 + %3 + %3 + %3 + %3 + %3 + %3 + %3 +\ %3 + %3 + %3 + %3 + %3 + %3 + %3 + %3 + %3 + %3 +\ %3 + %3 + %3 + %3 + %3 + %3 + %3 + %3 + %3 + %3 +\ %3 + %3 + %3 + %3 + %3 + %3 + %3 + %3 + %3 + %3;\ timer = tickcount() - timer;\ server_print("[AMXX]访问%-24s符号%d*50次,消耗%d毫秒", specifier, numberOfCycles, timer);\ return;\ } static counter, timer; g循环访问全局符号("new", v1) g循环访问全局符号("new public", v2) g循环访问全局符号("new public stock", v3) g循环访问全局符号("new public stock const", v4) g循环访问全局符号("new static", v5) g循环访问全局符号("new static stock", v6) g循环访问全局符号("new static stock const", v7) g循环访问全局符号("new stock", v8) g循环访问全局符号("new stock const", v9) g循环访问全局符号("new const", v10) g循环访问全局符号("public", v11) g循环访问全局符号("public stock", v12) g循环访问全局符号("public stock const", v13) g循环访问全局符号("static", v14) g循环访问全局符号("static stock", v15) g循环访问全局符号("static stock const", v16) g循环访问全局符号("stock", v17) g循环访问全局符号("stock const", v18) g循环访问全局符号("const", v19) g循环访问全局符号("enum", v20) g循环访问全局符号("enum member", v21) l循环访问局部符号("local const", const localSymbol=0, localSymbol) l循环访问局部符号("local enum", enum localSymbol{}, localSymbol) l循环访问局部符号("local enum member", enum _:localSymbol(+=0){lS_member}, lS_member) l循环访问局部符号("local new", new localSymbol, localSymbol) l循环访问局部符号("local new const", new const localSymbol, localSymbol) l循环访问局部符号("local static", static localSymbol, localSymbol) l循环访问局部符号("local static const", static const localSymbol, localSymbol) server_print("[AMXX]%s不是有效的声明方式", specifier); }

测试结果会受很多因素影响,比如服务器性能,插件加载的其他代码等,所以需要多次测试.以下结果仅供参考:

[AMXX]访问new 符号10000000*50次,消耗554毫秒 [AMXX]访问new public 符号10000000*50次,消耗550毫秒 [AMXX]访问new public stock 符号10000000*50次,消耗546毫秒 [AMXX]访问new public stock const 符号10000000*50次,消耗549毫秒 [AMXX]访问new static 符号10000000*50次,消耗548毫秒 [AMXX]访问new static stock 符号10000000*50次,消耗556毫秒 [AMXX]访问new static stock const 符号10000000*50次,消耗549毫秒 [AMXX]访问new stock 符号10000000*50次,消耗555毫秒 [AMXX]访问new stock const 符号10000000*50次,消耗548毫秒 [AMXX]访问new const 符号10000000*50次,消耗549毫秒 [AMXX]访问public 符号10000000*50次,消耗546毫秒 [AMXX]访问public stock 符号10000000*50次,消耗547毫秒 [AMXX]访问public stock const 符号10000000*50次,消耗552毫秒 [AMXX]访问static 符号10000000*50次,消耗550毫秒 [AMXX]访问static stock 符号10000000*50次,消耗553毫秒 [AMXX]访问static stock const 符号10000000*50次,消耗551毫秒 [AMXX]访问stock 符号10000000*50次,消耗550毫秒 [AMXX]访问stock const 符号10000000*50次,消耗546毫秒 [AMXX]访问const 符号10000000*50次,消耗134毫秒 [AMXX]访问enum 符号10000000*50次,消耗132毫秒 [AMXX]访问enum member 符号10000000*50次,消耗132毫秒 [AMXX]访问local const 符号10000000*50次,消耗134毫秒 [AMXX]访问local enum 符号10000000*50次,消耗134毫秒 [AMXX]访问local enum member 符号10000000*50次,消耗133毫秒 [AMXX]访问local new 符号10000000*50次,消耗566毫秒 [AMXX]访问local new const 符号10000000*50次,消耗554毫秒 [AMXX]访问local static 符号10000000*50次,消耗543毫秒 [AMXX]访问local static const 符号10000000*50次,消耗548毫秒

在以下代码中,由于省略了枚举标签和枚举名称,因此Eg1所储存的值默认为_:标签.

float函数要求参数1带有_:标签,因此不会触发Tag mismatch警告(标签不匹配).

floatround函数要求参数1带有Float:标签,因此会触发Tag mismatch警告(标签不匹配).

#include amxmodx public plugin_init() { enum { Eg1 } float(Eg1); floatround(Eg1); }

这种省略了枚举名称的声明方式,称为匿名枚举.

在以下代码中,由于省略了枚举标签,因此Example Eg1 Eg2所储存的值都是Example:标签.

GetPropertyValue函数要求参数1带有Example:标签,并根据该参数返回不同的值.

因此GetPropertyValue(192)将会触发Tag mismatch警告(标签不匹配).

#include amxmodx enum Example { Eg1, Eg2 } public plugin_init() { server_print("[AMXX]%s", GetPropertyValue(Eg1)); server_print("[AMXX]%s", GetPropertyValue(Eg2)); server_print("[AMXX]%s", GetPropertyValue(Example)); server_print("[AMXX]%s", GetPropertyValue(192)); } static GetPropertyValue(Example:propertyId) { new info[32]; if (propertyId == Eg1) return info = "身高180"; if (propertyId == Eg2) return info = "腰围180"; if (propertyId == Example) return info = "大错误"; return info = "极为严重的错误"; }

进入游戏载入地图后,将会在控制台看见以下四行文本:

[AMXX]身高190 [AMXX]腰围190 [AMXX]大错误 [AMXX]极为严重的错误

枚举名称常常作为标签使用,用于限定自定义函数所需的参数值.

当开发者在使用该函数,填写枚举成员作为参数时,会得到符合预期的结果.

若填写无关的值,开发者在看见标签不匹配的警告后,就会意识到自己犯了错误.

通常只需要全文搜索该标签即可找到该枚举声明,从而知晓应该填写什么作为参数.

比起填写1 2 3 4等意义不明的数字,以拥有标签检查的枚举成员作为参数能让代码可读性大幅提高.

这也是枚举常量最实用的一点.匿名枚举不提供标签检查,会失去这一优点,几乎退化为普通的常量或宏定义.

某些优秀的代码编辑器会通过查询枚举标签和枚举成员,为开发者提供代码建议.

比如在输入参数时,弹出一个菜单,内部显示所有枚举成员作为选项,或是拥有相同标签的函数.

开发者能轻易找到自己想要的选项.没了枚举标签,弹出的就可能是大量无关的常量选项.

3.3:增值法

以下代码将展示枚举声明中,增值法的使用例子.

增值法用于设定枚举成员的值如何增加.最常用的便是 <<= 运算符.

(<<= 1)能二进制层面,让每个成员的值左移1个位,右边补一个0.

比如Bf_Burn的值0b_0001,也就是十进制的1,左移1个位后变为0b_0010.

#include amxmodx enum Buff (<<= 0b_0000_0000_0001) { Bf_None, // 没有buff Bf_Burn = 1, // 燃烧buff Bf_Cold, // 寒冷buff Bf_Frozen, // 冻结buff Bf_Poison, // 中毒buff Bf_Hypoxia, // 缺氧buff Bf_Blind, // 失明buff Bf_SpeedUp, // 加速buff Bf_Rage, // 狂暴buff Bf_SuperArmor, // 霸体buff Bf_MImmunity, // 免疫魔法buff Bf_PImmunity, // 免疫物攻buff } public plugin_init() { // 注意,AMXX 1.8.2或低版本无法使用%b按照二进制形式打印文本. // 打印结果: [AMXX]0000 0b_0000_0000_0000 server_print("[AMXX]%04d %012b", Bf_None, Bf_None); // 打印结果: [AMXX]0001 0b_0000_0000_0001 server_print("[AMXX]%04d %012b", Bf_Burn, Bf_Burn); // 打印结果: [AMXX]0002 0b_0000_0000_0010 server_print("[AMXX]%04d %012b", Bf_Cold, Bf_Cold); // 打印结果: [AMXX]0004 0b_0000_0000_0100 server_print("[AMXX]%04d %012b", Bf_Frozen, Bf_Frozen); // 打印结果: [AMXX]0008 0b_0000_0000_1000 server_print("[AMXX]%04d %012b", Bf_Poison, Bf_Poison); // 打印结果: [AMXX]0016 0b_0000_0001_0000 server_print("[AMXX]%04d %012b", Bf_Hypoxia, Bf_Hypoxia); // 打印结果: [AMXX]0032 0b_0000_0010_0000 server_print("[AMXX]%04d %012b", Bf_Blind, Bf_Blind); // 打印结果: [AMXX]0064 0b_0000_0100_0000 server_print("[AMXX]%04d %012b", Bf_SpeedUp, Bf_SpeedUp); // 打印结果: [AMXX]0128 0b_0000_1000_0000 server_print("[AMXX]%04d %012b", Bf_Rage, Bf_Rage); // 打印结果: [AMXX]0256 0b_0001_0000_0000 server_print("[AMXX]%04d %012b", Bf_SuperArmor, Bf_SuperArmor); // 打印结果: [AMXX]0512 0b_0010_0000_0000 server_print("[AMXX]%04d %012b", Bf_MImmunity, Bf_MImmunity); // 打印结果: [AMXX]1024 0b_0100_0000_0000 server_print("[AMXX]%04d %012b", Bf_PImmunity, Bf_PImmunity); // 打印结果: [AMXX]2048 0b_1000_0000_0000 server_print("[AMXX]%04d %012b", Buff, Buff); // 打印结果: [AMXX]0011 0b_0000_0000_1011 new Buff:IHaveMultipleBuffs = Bf_Burn | Bf_Cold | Bf_Poison; server_print("[AMXX]%04d %012b", IHaveMultipleBuffs, IHaveMultipleBuffs); }

进入游戏,载入地图后,可以看见这些枚举常量的十进制,二进制对应的数值.

第一个成员Bf_None默认为0,除非写上初始化表达式,比如第二个成员Bf_Burn被设为1,二进制是0b_0001.

增值法(<<= 1)成功使其余成员的值等于上一个成员的左移.

枚举名称的值等于最后一个成员的值使用增值法之后的运算结果.

使用 <<= *= 运算符作为增值法时,需要注意,0的左移结果永远是0,0的乘法结果永远是0.

因此需要为某个成员初始化为任意非0值.使后续成员拥有不同的值.

增值法是(<<= 1)的枚举声明,通常也叫做二进制标志位枚举.或标志位枚举.

通常用于表示一个对象拥有同时拥有的多个状态.

比如玩家同时拥有燃烧,寒冷,中毒状态.用3个变量表示会很麻烦.

若使用二进制,则可以用一个变量表示32种状态(AMXX仅支持32位整数).

代码中的IHaveMultipleBuffsBf_Burn Bf_Cold Bf_Poison进行"按位或运算"的结果.

观察它的二进制数值,可以看见右端的1011,实际上就是Bf_Burn的0001,Bf_Cold的0010,Bf_Poison的1000.

使用"按位与运算",便可确认一个变量或常量是否含有某个位:

if (IHaveMultipleBuffs & Bf_Burn) { server_print("[AMXX]IHaveMultipleBuffs含有Bf_Burn") } else { server_print("[AMXX]IHaveMultipleBuffs不含有Bf_Burn") }

使用"按位或运算",便可添加状态(按照或运算的特性,已有状态不会重复添加):

IHaveMultipleBuffs |= Bf_MImmunity; server_print("[AMXX]变量IHaveMultipleBuffs添加了(魔法免疫)状态"); IHaveMultipleBuffs |= Bf_Burn | Bf_Cold | Bf_Poison; server_print("[AMXX]变量IHaveMultipleBuffs添加了(燃烧,寒冷,剧毒)状态");

使用"按位或运算"和"按位取反",便可删除状态(按照或运算的特性,不会重复删除不存在的状态):

IHaveMultipleBuffs &= ~Bf_MImmunity; server_print("[AMXX]变量IHaveMultipleBuffs删除了Bf_MImmunity(魔法免疫)状态"); IHaveMultipleBuffs &= ~(Bf_Burn | Bf_Cold | Bf_Poison); server_print("[AMXX]变量IHaveMultipleBuffs删除了(燃烧,寒冷,剧毒)状态");

匿名枚举同样可以设定增值法.但失去了标签检查,会让开发者或第三方使用者产生疑惑.

可能会不小心给变量添加或删除无关的二进制数值.

某些优秀的代码编辑器会通过查询枚举标签和枚举成员,为开发者提供代码建议.

比如在输入 &= 之后,弹出一个菜单,内部显示所有枚举成员作为选项,或是拥有相同标签的函数.

开发者能轻易找到自己想要的选项.没了枚举标签,弹出的就可能是大量无关的常量选项.

AMXX的头文件中有大量反面教材,各种宏定义常量,比如CSW_*系列常量,ADMIN_*系列常量.各种匿名枚举,比如print_*系列常量.

它们都是默认使用_:标签,相关函数的返回值,函数的参数,统统使用_:标签.

函数注释又写得简略,即便填写了无关的数值也没有警告,给开发者带来各种困扰.

应当善加利用枚举名称,标签检查可大幅度降低查找问题的时间.

3.4:元素标签

定义数组容量或用索引访问元素时,索引与容量的标签必须一致.

枚举成员的元素标签是为数组服务的.此时整个枚举声明可以视作枚举结构体.

在以下代码中,这些拥有元素标签的枚举成员将改变编译器对数组元素的标签判断.

#include amxmodx enum { Float: Eg1_float, // Eg1_float在增值法作用下,存有常量0 Array: Eg2_array, // Eg2_array在增值法作用下,存有常量1 } stock gArray1[2]; // 声明整数型数组 stock Float:gValueFloat, Array:gValueArray; public plugin_init() { // 将0号元素赋值为浮点数512.5,会被编译器警告:标签不匹配.因为gArray1是整数型数组 gArray1[0] = 512.5; // 用Eg1_float代替0号索引,不被警告.因为Eg1_float带有元素标签Float: gArray1[Eg1_float] = 512.5; // 将1号元素赋值为Invalid_Array,会被编译器警告:标签不匹配.因为gArray1是整数型数组 gArray1[1] = Invalid_Array; // 用Eg2_array代替1号索引,不被警告.因为Eg2_array带有元素标签Array: gArray1[Eg2_array] = Invalid_Array; // 注:Invalid_Array是一个拥有Array:标签的常量. // 注:AMXX1.8.2或低版本有bug.因此,上面元素的赋值表达式依然会被警告 // 用浮点型变量储存0号元素值,会被编译器警告.因为gArray1是整数型数组 gValueFloat = gArray1[0]; // 用Eg1_float代替0号索引,不被警告.因为Eg1_float带有元素标签Float: gValueFloat = gArray1[Eg1_float]; // 用Array型变量储存1号元素值,会被编译器警告.因为gArray1是整数型数组 gValueArray = gArray1[1]; // 用Eg2_array代替1号索引,不被警告.因为Eg2_array带有元素标签Array: gValueArray = gArray1[Eg2_array]; }

元素标签可让数组储存多种不同类型的元素数值.

如果要存取一个对象的多种不同类型数据,

比起声明多个不同类型的变量用于存取,这种方法会更加方便.

元素标签通常应该与枚举名称一起使用.匿名枚举只能影响访问时的标签检查.

将枚举名称作为容量使用,数组初始化时也能设定不同类型的元素值.如下所示:

#include amxmodx enum Example { Float: Eg1_float, Array: Eg2_array, } // 不被警告.编译器承认其内2个元素使用Float:与Array:标签 stock gArray1[Example] = { 5.55, Invalid_Array }; // 触发2个警告,编译器认为其内2个元素应使用_:标签 stock gArray2[2] = { 5.55, Invalid_Array }; // 无关内容: public plugin_init() { server_print("[AMXX]gArray1 = [ %.2f, %d ]", gArray1[Eg1_float], gArray1[Eg2_array]); server_print("[AMXX]gArray2 = [ %.2f, %d ]", gArray2[0], gArray2[1]); }

声明数组时将枚举名称作为容量,开发者或第三方人员在访问元素时,会自然而然的去查阅并使用相关枚举成员.

填写无关的数值作为索引,会触发警告.使访问者在编译时意识到自己的错误,这减少了运行时出错的概率.

AMXX的头文件中有大量反面教材,各种宏定义常量,比如CSW_*系列常量,ADMIN_*系列常量.各种匿名枚举,比如print_*系列常量.

它们都是默认使用_:标签,相关函数的返回值,函数的参数,统统使用_:标签.

函数注释又写得简略,即便填写了无关的数值也没有警告,给开发者带来各种困扰.

应当善加利用枚举名称,标签检查可大幅度降低查找问题的时间.

注:

匿名枚举中,枚举常量默认带有_:标签,拥有枚举名称之后,枚举常量带有Example:标签.

数组容量与元素索引的标签不同将会触发警告:

#include amxmodx enum Example { Float: Eg1_float, Array: Eg2_array, } // 容量的标签被省略时,默认为_:标签 stock gArray1[2]; // 以Example:作为容量标签(注:初始化触发2个警告.或许是bug,或者,强制更改2的标签本身属于违规行为,正常应该直接填写Example而不是Example:2) stock gArray2[Example:2] = { 5.5, Invalid_Array }; public plugin_init() { // 被警告,因为gArray1的容量标签是_: server_print("[AMXX]gArray1[0] = %.2f", gArray1[Eg1_float]); // 不被警告,因为gArray2的容量标签是Example: server_print("[AMXX]gArray2[0] = %d", gArray2[Eg2_array]); }

3.5:枚举成员名称

枚举成员与枚举名称一样,是无地址的常量.遵从标识符的命名规则.

命名后时应该自行检查,是否有多个成员重名.编译器不作检查.

#include amxmodx // 多个成员重名,不会被警告或触发报错 enum Example1 { Eg1, Eg1, Eg1, Eg1, } // 与其它枚举的成员重名,不会被警告或触发报错 enum Example2 { Eg1, Eg1, Eg1, Eg1, } // 无关内容: public plugin_init() { }

3.6:元素尺寸

枚举成员的元素尺寸是为数组服务的.此时整个枚举声明可以视作枚举结构体.

在以下代码中,这些拥有元素尺寸的枚举成员,能将数组多个数据整合为1个块,视作1个元素.

#include amxmodx // 枚举结构体:设定"人"的数据结构 enum CPeople { CPName[32], // 数组用中32个元素表示"人"的姓名 CPBirthDate[32], // 数组用中32个元素表示"人"的出生日期 CPGender[16], // 数组用中16个元素表示"人"的性别 CPHeight[22], // 数组用中22个元素表示"人"的身高 CPWeight[22], // 数组用中22个元素表示"人"的体重 } public plugin_init() { new people[CPeople] = { "李华", "9:56 2023/9/23", "武装直升机", "3米", "5000公斤" }; server_print("[AMXX]某人的姓名:%s", people[CPName]); server_print("[AMXX]某人的出生日期:%s", people[CPBirthDate]); server_print("[AMXX]某人的性别:%s", people[CPGender]); server_print("[AMXX]某人的身高:%s", people[CPHeight]); server_print("[AMXX]某人的体重:%s", people[CPWeight]); }

以上代码看起来像是把一维数组people升维至二维数组.但那只是表象.

从第2个枚举成员开始,每个成员的值等于上一个成员的值+元素尺寸.

CPName的值是0,元素尺寸是32,因此CPBirthDate的值是0+32.

如此一来枚举名称CPeople的值为32+32+16+22+22,即124.而new people[CPeople]实际上是声明了尺寸为124的一维数组.

枚举名称作为数组的容量使用,能够改变数组初始化表达式的数据分配规则.

people数组的0至31号元素被用于储存"李华".

people数组的32至63号元素被用于储存"9:56 2023/9/23".

people数组的64至79号元素被用于储存"武装直升机".

people数组的80至101号元素被用于储存"3米".

people数组的102至123号元素被用于储存"5000公斤".

虽然people数组中依然拥有124个元素,但用枚举成员作为索引访问people数组时,可以将people数组视作拥有5个元素.

这种情况下,枚举成员的元素尺寸,实际上就是这5个元素的尺寸.

若字符串的字节数超过设定的元素尺寸,会覆盖下一个元素的储存空间.

若字符串的字节数小于元素尺寸,多出来的部分全部默认储存0.

people数组能表现出二维数组的特性,可以拥有2对方括号.

#include amxmodx // 枚举结构体:设定"数组包"的数据结构 enum CArray { CA1, // 数组用中1个元素表示"数组包"的第1个包 CA2[1], // 数组用中1个元素表示"数组包"的第2个包 CA3[2], // 数组用中2个元素表示"数组包"的第3个包 CA4[3], // 数组用中3个元素表示"数组包"的第4个包 } public plugin_init() { new array[CArray] = { 1, 2, { 3, 4 }, "56" }; server_print("[AMXX]CA1数组包的值:%d", array[CA1]); // 元素尺寸小于2,表现为单值 server_print("[AMXX]CA2数组包的值:[ %d ]", array[CA2]); // 元素尺寸小于2,表现为单值 server_print("[AMXX]CA3数组包的值:[ %d, %d ]", array[CA3][0], array[CA3][1]); // 元素尺寸大于1,表现为二维数组 server_print("[AMXX]CA4数组包的值:[ %c, %c, %d ]", array[CA4][0], array[CA4][1], array[CA4][2]); // 元素尺寸大于1,表现为二维数组 }

打印结果为:

[AMXX]CA1数组包的值:1 [AMXX]CA2数组包的值:[ 2 ] [AMXX]CA3数组包的值:[ 3, 4 ] [AMXX]CA4数组包的值:[ 5, 6, 0 ]

3.7:初始化表达式

正常情况下由增值法对枚举成员和枚举名称进行初始化.

增值法设定的常量值是规律的.若需要一些不规律的常量值,便可用初始化表达式来设置.

表达式中不能有浮点数,数组,字符串这类字面量.

float.inc提供了浮点数运算符的重载,大部分运算符都被替换成运行时函数.

因此,编译器无法在编译时确定浮点数相关的运算结果.

#include amxmodx enum Example { Eg1 = 1024, // 可行的,无警告或报错 Eg2 = 0b10, // 可行的,无警告或报错 Eg3 = 0x1F, // 可行的,无警告或报错 Eg4 = 3.5, // 可行的,无警告或报错(枚举体内无标签检查),浮点数自动转变为指针1080033280 Eg5 = _:5.0, // 可行的,无警告或报错,浮点数手动转变为指针1084227584 Eg6 = _:1.5 + 5,// 得到1069547525 Eg7 = 1 + 2.0, // 报错,浮点运算无法在编译时进行 Eg8 = 1.0 + 5, // 报错,浮点运算无法在编译时运行 } // 无关内容: public plugin_init() { }

名称成员储存的值拥有什么标签,由枚举标签决定.

如果没有枚举标签,则使用枚举名称作为标签.

如果也没有枚举名称,则默认使用_:作为标签.

具体参考枚举标签和枚举名称的介绍.