Skip to content

枚举

字数: 0 字 阅读时间: 0 分钟

C 大师 182 184

其实枚举不算是太复杂的数据类型,但是很多人对枚举并不了解。你可能觉得枚举没什么用,但其实枚举非常强大。枚举说白了就是列举一组常量,方便阅读与调试。

枚举相比与直接用 #define ,会更方便。要注意的是枚举的成员是以逗号结尾,这一点与结构体和联合体不同。

如果不定义成员初值,默认从 0 开始递增。如果中间有个成员定义初值,后面的成员会从这个成员的值开始递增。

C
enum {
    MEMBER1, /* = 0 */
    MEMBER2, /* = 1 */
    MEMBER3, /* = 2 */
};

enum {
    MEMBER1 = 1,
    MEMBER2, /* = 2 */
    MEMBER3, /* = 3 */
};

enum {
    MEMBER1, /* = 0 */
    MEMBER2, /* = 1 */
    MEMBER3, /* = 2 */
    MEMBER4 = 1,
    MEMBER5, /* = 2 */
    MEMBER6  /* = 3 */
};

枚举也是一种数据类型,用枚举可以定义变量。那你可能会问枚举这种类型有什么用呢?枚举就是把变量所有可能取的值列出来,并用标识符标记。主要定义一些有限的事物,比如星期、月份、状态码、错误码等。

当一个变量只有几种取值的时候,就可以考虑用枚举。枚举在调试时显示当前枚举所定义的标识符,而宏定义只有一个数字。

枚举常常与 switch-case 搭配使用。 switch-case 的意思是根据不同的情况做不同的事情,那么我们就可以用枚举来定义不同的情况,然后用 switch-case 处理。下面举一个案例:

C
#include <stdio.h>
#include <string.h>

typedef enum {
    ROOT = 0U,
    ADMIN,
    USER,
    GUEST
} identity_t;

typedef struct {
    char name[10];
    char password[30];
    identity_t identity;
} user_t;

void delete_root_file(user_t *user) {
    char password[30];
    printf("User name: %s\n", user->name);

    puts("Please Input password: ");
    gets(password);
    if (strcmp(password, user->password)) {
        puts("Password error! \n");
        return;
    }

    switch (user->identity) {
        case ROOT:
        case ADMIN: {
            puts("Deleted root file. \n");
        } break;

        case USER:
        case GUEST: {
            puts("Permission Denied. \n");
        } break;

        default: {
            puts("Unknown User. \n");
        } break;
    }
}

int main(void) {
    user_t root = {"root", "123456", ROOT};
    user_t user = {"user", "123456", USER};
    user_t guest = {"guest", "123456", GUEST};

    delete_root_file(&root);
    delete_root_file(&user);
    delete_root_file(&guest);

    return 0;
}

运行结果:

bash
User name: root
Please Input password:
123456
Deleted root file.

User name: user
Please Input password:
123456
Permission Denied.

User name: guest
Please Input password:
123456
Permission Denied.

这里用枚举简单定义了几个用户权限级别,只有 ROOT, ADMIN 级别的用户才可以删除 root 文件,其他用户操作提示 Permission Denied. ,当然这个案例比较简单,你可以带到实际的程序中去思考怎么使用枚举。

既然枚举可以定义情况,那么我们就可以用枚举定义状态实现状态机。参照软件工程常用方法。

如果你还是觉得枚举没什么用,多说无用,我们来看看 freemodbus 的源码:

C
/* https://github.com/cwalter-at/freemodbus/blob/f16701094ca64df3a0366dde1c186a55976d61e4/modbus/rtu/mbrtu.c#L61 */
typedef enum
{
    STATE_TX_IDLE,              /*!< Transmitter is in idle state. */
    STATE_TX_XMIT               /*!< Transmitter is in transfer state. */
} eMBSndState;

static volatile eMBSndState eSndState;

/* https://github.com/cwalter-at/freemodbus/blob/f16701094ca64df3a0366dde1c186a55976d61e4/modbus/rtu/mbrtu.c#L284 */
BOOL
xMBRTUTransmitFSM( void )
{
    BOOL            xNeedPoll = FALSE;

    assert( eRcvState == STATE_RX_IDLE );

    switch ( eSndState )
    {
        /* We should not get a transmitter event if the transmitter is in
         * idle state.  */
    case STATE_TX_IDLE:
        /* enable receiver/disable transmitter. */
        vMBPortSerialEnable( TRUE, FALSE );
        break;

    case STATE_TX_XMIT:
        /* check if we are finished. */
        if( usSndBufferCount != 0 )
        {
            xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );
            pucSndBufferCur++;  /* next byte in sendbuffer. */
            usSndBufferCount--;
        }
        else
        {
            xNeedPoll = xMBPortEventPost( EV_FRAME_SENT );
            /* Disable transmitter. This prevents another transmit buffer
             * empty interrupt. */
            vMBPortSerialEnable( TRUE, FALSE );
            eSndState = STATE_TX_IDLE;
        }
        break;
    }

    return xNeedPoll;
}

这里用枚举定义了发送状态,然后用 switch-case 来进行处理。枚举用来处理状态、错误时非常有用,而且非常容易扩展,多用就知道它的好处了。

Powered by VitePress, deployed by Github & Vercel.