• 0 票 - 平均分 0
  • 1
  • 2
  • 3
  • 4
  • 5
[教程] 枚举器 enum 详细讲解 原文作者: iPLEOMAX
#1

枚举器



原文作者: iPLEOMAX

关于枚举,有些细节很多脚本作者并不清楚。

很多人都喜欢在脚本里用枚举来存玩家数据、车辆数据、房屋数据之类的,尤其是用户信息这块。

一个很典型的写法是这样的:

代码:

enum E_PLAYER_INFO
{
    SCORE,
    MONEY,
    KILLS,
    DEATHS
};

new pInfo[MAX_PLAYERS][E_PLAYER_INFO];

用起来大概是这样:

代码:

public OnPlayerDeath(playerid, killerid, reason)
{
    pInfo[playerid][DEATHS]++;
    
    if(IsPlayerConnected(killerid) && killerid != playerid)
        pInfo[killerid][KILLS]++;
    
    return 1;
}

这段应该不难理解。

现在我们看另一个例子:

代码:

enum E_PLAYER_INFO
{
    SCORE,
    MONEY = 9,
    KILLS = 5,
    DEATHS = 56
};

new pInfo[MAX_PLAYERS][E_PLAYER_INFO];

printf("%i | %i | %i | %i", pInfo[0][SCORE], pInfo[0][MONEY], pInfo[0][KILLS], pInfo[0][DEATHS]);

你可能会觉得输出是 0 | 9 | 5 | 56,对吧?

如果你这么想,那就错了。实际上输出是 0 | 0 | 0 | 0

你可能会以为枚举是用来存储数据的,但事实并非如此。我在枚举里写了 MONEY = 9,然后在 printf 里用了 pInfo[0][MONEY],但编译器其实把它当成 pInfo[0][9] 来处理,而不是什么 E_PLAYER_INFO:MONEY。也就是说,MONEY 在这里只是作为一个索引来用的。

所以枚举本质上根本不是变量,它其实就是一组常量,只不过帮你的数组索引起了个好记的名字罢了。

好,我们再来深入一点,应该能让你理解得更清楚。

举个例子:

代码:

const e_CAR1 = 0;
const e_CAR2 = 1;
const e_CAR3 = 2;

new MyCars[3];

main()
{
    MyCars[e_CAR1] = 520;
    MyCars[e_CAR2] = 458;
    MyCars[e_CAR3] = 411;
}

现在我们用枚举重写一下:

代码:

enum e_TEST
{
    e_CAR1,
    e_CAR2,
    e_CAR3
};

new MyCars[e_TEST];

main()
{
    MyCars[e_CAR1] = 520;
    MyCars[e_CAR2] = 458;
    MyCars[e_CAR3] = 411;
}

这两段代码都能正常编译,做的事情也完全一样。你看出区别了吗?其实就只是代码写法不同而已。

这个例子告诉我们,枚举干的事情和常量是一样的。不过枚举有些地方比常量或者宏定义(const e_CAR1 = 0; 或者 #define e_CAR1 0)更好用,具体差异我们后面再说。

首先你需要明白枚举的结构:

代码:

enum TEST
{
   Abc,
   Def,
   Ghi,
};

Abc 是一个值为 0 的常量(差不多等于 const Abc = 0;#define Abc 0),Def 是值为 1 的常量,Ghi 是值为 2 的常量。

预编译器会自动给这些常量赋值,从 0 开始一直往上排。而用常量的时候,你得自己手动赋值。枚举则是自动帮你排好的。

再看个例子:

代码:

enum TEST
{
   e_ONE,      // 自动得到 0
   e_TWO,      // 得到 1
   e_THREE,    // 得到 2
   e_FOUR = 12,// 这里手动赋了 12,所以不再是 3 了
   e_FIVE,     // 变成 13,因为上一个值是 12
   e_SIX       // 变成 14,上一个值是 13
}

看到了吧,如果你手动给某个枚举项赋了值,后面的项就会在此基础上依次加 1。

再来看看更复杂的情况:

代码:

enum DATA
{
    INT,            // 得到 0
    STRING[10],     // 得到 1,但因为是数组,一个位置不够,它会占 10 个位置
                    // 所以实际占用的索引是 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
    INT2,           // 得到 11,因为 STRING 最后一位是 10
    STRING2[10]     // 从 12 开始,一直到 22,需要 10 个位置
};

运行下面的代码:

代码:

printf("%i", _:DATA);

结果输出 22

因为整个枚举的总大小是 22:
INT 占 1 个 + STRING 占 10 个 + INT2 占 1 个 + STRING2 占 10 个 = 22。

你可能会问,为什么我写 INT 是 1 而不是 0?因为这里我数的是占用了多少个位置,不是从哪个位置开始。如果问起始位置,那 INT 确实是 0。

一个大小为 22 的块,索引范围是 0 到 21,总共 22 个位置。

再试一下:

代码:

printf("%i", _:STRING);

输出 1。因为第一个位置(索引 0)被 INT 占了,所以 STRING 从索引 1 开始。

每个枚举项占用的位置是这样的:
  • INT:索引 0
  • STRING:索引 1 到 10
  • INT2:索引 11
  • STRING2:索引 12 到 21

运行这段:

代码:

new Array[DATA];
printf("%i", sizeof Array);

结果也是 22,因为 DATA 的大小就是 22。

所以,这个数组实际上相当于:
代码:

Array[INT + STRING + INT2 + STRING2];
这行代码是编译不了的,只是帮你想明白结构而已。

也就是说,用枚举的时候,你其实是用这些枚举常量作为数组的索引。



另一个例子



代码:

enum TEST
{
    SomeInteger = 124,
    SomeString[12],
    Float:SomeFloat
};

public OnFilterScriptInit()
{
    new var_TEST[TEST];
    
    var_TEST[SomeInteger] = 1337;
    printf("1) 储存的值: %i, 常量: %i", var_TEST[SomeInteger], _:SomeInteger);
    
    format(var_TEST[SomeString], 12, "Hey!");
    printf("2) 储存的值: %s, 常量: %i", var_TEST[SomeString], _:SomeString);
    
    var_TEST[SomeFloat] = 2054.124;
    printf("3) 储存的值: %f, 常量: %i", var_TEST[SomeFloat], _:SomeFloat);
    
    return true;
}

输出结果:
  1. 储存的值: 1337, 常量: 124
  2. 储存的值: Hey!, 常量: 125
  3. 储存的值: 2054.124023, 常量: 137

第一行,SomeInteger 的常量值是 124,我把 1337 存到了索引 124 的位置。

第二行,SomeString 虽然定义了长度为 12,但输出常量值时显示的是 125。因为 125 是这个字符串的起始索引,它占了 125 到 136 这 12 个位置。

第三行,浮点数正常显示,但 SomeFloat 的常量值是 137。你可能会想,它应该在 SomeString(125)后面,应该是 126 才对?但别忘了 SomeString[12] 是个数组,占了 12 个位置,所以 SomeFloat 的起始索引是 125 + 12 = 137。



一些额外的重要信息



枚举也可以作为标签使用:

代码:

enum E_MY_TAG
{
    E_FIRST = 4,
    E_SECOND = 2
}

然后可以这样写:
代码:

new E_MY_TAG:SomeVar;
或者:
代码:

new E_MY_TAG:MyVariable = E_FIRST; // 不会报错,因为 E_FIRST 本来就是 E_MY_TAG 的一部分

但是:
代码:

new data[E_MY_TAG];
data[E_FIRST] = 7; // 编译错误,索引越界
因为 E_MY_TAG 的大小是 3(枚举里最大索引是 2),而 E_FIRST 的值是 4,已经超出范围了。

匿名枚举:
代码:

enum // 没有名字
{
    E_TEST[10] = 32,
    E_VAR
};

main()
{
    new data[E_TEST]; // E_TEST 的值是 32,不是 10
}

强标签和弱标签:
代码:

enum E_STRONG // 名字以大写字母开头,生成的是强标签
{
    E_VAR = 64
};

main()
{
    new Test = E_STRONG:E_VAR;
    // 会报“标签不匹配”的警告,因为 Test 没有 E_STRONG 标签
}

代码:

enum e_WEAK // 名字以小写字母开头,生成的是弱标签
{
    E_VAR = 64
};

main()
{
    new Test = e_WEAK:E_VAR;
    // 没有警告
}



枚举的大概内容就是这些了,这也是为什么我们更喜欢用枚举而不是直接写常量的原因。

感谢 Y_Less 的讲解。
  回复


此主题中的消息
[教程] 枚举器 enum 详细讲解 原文作者: iPLEOMAX - 由 小鸟unsigned - 03-22-2026, 12:11 AM

论坛跳转:


浏览此主题的用户: 1 位客人