c语言之枚举类型(enum)

               I.  enum枚举类型

1 . 如果一个变量只有几种可能的值,则可以定义为“枚举类型”;所谓“枚举”就是把可能的值一一的列举出来,变量的值只限于列举出来的值的范围, 如:

	语法:
	enum 枚举类型{枚举成员列表};
	//其中的枚举成员列表是以逗号“,”相分隔
	如:
    enum Spectrum{red,balck,yellow,blue,white};

时间:2018-10-24 修改,Spectrum枚举变量中成员balck写错了,应为black。多谢 [福葱] 读者的提示!

enum Spectrum{red,balck,yellow,blue,white};

 enum Spectrum{red,black,yellow,blue,white};
 其中:enum为关键字, Spectrum为该枚举类型的标记名(标记名:遵循标识符的命名规则);
 花括号“{}”中的red,black,yellow,blue,white称为“枚举元素”或“枚举常量”;

2.可以用“枚举类型”声明符号名称来表示int型常量。只要是能使用int型的地方就能够使用枚举类型。注意:C语言中的枚举的一些特性不适合C++;比如c中的枚举变量允许使用++运算符,但是c++中则不允许。

3.有关枚举类型常量的默认值
这里仍然拿上面的枚举类型常量作为示例:

    enum Spectrum{red,balck,yellow,blue,white};
                   0    1     2     3     4 
默认情况下:该枚举列表中的常量值分别为:0,1,2,3,4

当然也可以在枚举声明中,可以为枚举常量指定整数值:如

   enum Spectrum{red=10,balck=20,yellow=30,
                 blue=40,white=50};

也可以只对枚举类型中的一个枚举元素赋值;这时该枚举元素后面的枚举元素会被赋予后续的值。如:

    enum Spectrum{red,balck=22,yellow,blue,white};
    
  则:red=0,black=22,yellow=23,blue=24,white=25;

2018-04-26 07:04:01 补充

4.枚举类型也可以是匿名的
匿名的枚举类型会有着意想不到的作用,比如在程序中需要使用数值的名字的时候,常常有3种方法可以实现。
(1)宏定义

#define FALSE 0
#define TRUE  1

但是宏定义有弱点:其定义的只是预处理阶段的名字,在编译器的预处理阶段会进行简单的名字替换,而且不会进行类型的安全检查,其作用域是全局的,因此若程序中有变量true、false,则会被替换。为了避免这样的情况,采用将全部的宏用大写来命名,以区别于正常的代码。

(2)匿名的enum枚举

enum {FALSE,TRUE};		//FALSE 0, TRUE 1

这里的FALSE,TRUE都是编译时期的名字,编译器会对其进行类型检查,若代码中其他地方有和该名字冲突的,会报错。因此采用匿名的枚举不会有产生干扰正常代码的尴尬。

(3)采用静态变量

c++中更加推荐使用该方式,如:

static const int FALSE = 0;
static const int TRUE = 1;

在这里的TRUE,FALSE同样会得到编译器在编译阶段的检查。由于是静态变量,因此其作用域被限制到了本文件内。相比于enum枚举类型,静态变量不仅是编译时期的名字,同时编译器还可能会在代码中为TRUE,FALSE产生实际的数据,这会增加一点存储空间。


5.枚举类型的作用:提高程序的可读性和可维护性

示例代码一:输出12个月的英文单词

#include<stdio.h>
#include <stdlib.h>
enum Months{jan=1,feb,mar,apr,may,jun,jul,aug,sep,oct,nov,dec};
int main(void)
  {
  enum Months m;
    //flag用来标记输出的个数,若为4个,则输出一个换行符
  int flag=0;  
    //指针数组,存放的是个字符串的入口地址
  char *months[]={"January","February","March","April","May","June",
        "July","August","September","October","November","December"};
  for (m=jan;m<=dec;m++)
  {
    printf ("%-d月份:%-10s ",m,months[m-1]);
    flag++;
    if (flag%4==0)
    {
      putchar('\n');
    }
  }
  system ("pause");
  return 0;
  }

示例代码二:用作函数返回错误码

这是在编程中使用得最为广泛的用法之一,通常在开发中,我们把考虑到的所有可能出现的错误都列举在一个enum中,这样,我们就不用再函数调用出错时候,返回一个数字。这样的做法好处有很多:如更容易看出函数调用出错的原因、如果enum中有漏掉的错误提示,则再添加一个等。

typedef enum {
	/* Internal errors to rdkafka: */
	RD_KAFKA_RESP_ERR__BEGIN = -200,     /* begin internal error codes */
	RD_KAFKA_RESP_ERR__BAD_MSG = -199,   /* Received message is incorrect */
	RD_KAFKA_RESP_ERR__BAD_COMPRESSION = -198, /* Bad/unknown compression */
	RD_KAFKA_RESP_ERR__DESTROY = -197,   /* Broker is going away */
	RD_KAFKA_RESP_ERR__FAIL = -196,      /* Generic failure */
	RD_KAFKA_RESP_ERR__TRANSPORT = -195, /* Broker transport error */
	RD_KAFKA_RESP_ERR__CRIT_SYS_RESOURCE = -194, /* Critical system resource
						      * failure */
	RD_KAFKA_RESP_ERR__RESOLVE = -193,   /* Failed to resolve broker */
	RD_KAFKA_RESP_ERR__MSG_TIMED_OUT = -192, /* Produced message timed out*/
	RD_KAFKA_RESP_ERR__PARTITION_EOF = -191, /* Reached the end of the
						  * topic+partition queue on
						  * the broker.
						  * Not really an error. */
	RD_KAFKA_RESP_ERR__UNKNOWN_PARTITION = -190, /* Permanent:
						      * Partition does not
						      * exist in cluster. */
	RD_KAFKA_RESP_ERR__FS = -189,        /* File or filesystem error */
	RD_KAFKA_RESP_ERR__UNKNOWN_TOPIC = -188, /* Permanent:
						  * Topic does not exist
						  * in cluster. */
	RD_KAFKA_RESP_ERR__ALL_BROKERS_DOWN = -187, /* All broker connections
						     * are down. */
	RD_KAFKA_RESP_ERR__INVALID_ARG = -186,  /* Invalid argument, or
						 * invalid configuration */
	RD_KAFKA_RESP_ERR__TIMED_OUT = -185,    /* Operation timed out */
	RD_KAFKA_RESP_ERR__QUEUE_FULL = -184,   /* Queue is full */
        RD_KAFKA_RESP_ERR__ISR_INSUFF = -183,   /* ISR count < required.acks */
	RD_KAFKA_RESP_ERR__END = -100,       /* end internal error codes */

	/* Standard Kafka errors: */
	RD_KAFKA_RESP_ERR_UNKNOWN = -1,
	RD_KAFKA_RESP_ERR_NO_ERROR = 0,
	RD_KAFKA_RESP_ERR_OFFSET_OUT_OF_RANGE = 1,
	RD_KAFKA_RESP_ERR_INVALID_MSG = 2,
	RD_KAFKA_RESP_ERR_UNKNOWN_TOPIC_OR_PART = 3,
	RD_KAFKA_RESP_ERR_INVALID_MSG_SIZE = 4,
	RD_KAFKA_RESP_ERR_LEADER_NOT_AVAILABLE = 5,
	RD_KAFKA_RESP_ERR_NOT_LEADER_FOR_PARTITION = 6,
	RD_KAFKA_RESP_ERR_REQUEST_TIMED_OUT = 7,
	RD_KAFKA_RESP_ERR_BROKER_NOT_AVAILABLE = 8,
	RD_KAFKA_RESP_ERR_REPLICA_NOT_AVAILABLE = 9,
	RD_KAFKA_RESP_ERR_MSG_SIZE_TOO_LARGE = 10,
	RD_KAFKA_RESP_ERR_STALE_CTRL_EPOCH = 11,
	RD_KAFKA_RESP_ERR_OFFSET_METADATA_TOO_LARGE = 12
} rd_kafka_resp_err_t;


上面的代码是 kafka C客户端中的源码,即列举出kafka 客户端中可能会出现的错误的原因。通常,还会实现2个函数,如:

//1.返回便于人们阅读的错误字符串
const char *rd_kafka_err2str (rd_kafka_resp_err_t err); 
//该函数的实现很简单,即以字符串形式打印error,如:
const char *rd_kafka_err2str (rd_kafka_resp_err_t err)
{
    char *tempStr = NULL;
    //对err的判断
    switch(err)
    {
    case RD_KAFKA_RESP_ERR__BEGIN : tempStr = "RD_KAFKA_RESP_ERR__BEGIN";break;
        //或: case RD_KAFKA_RESP_ERR__BEGIN: return "RD_KAFKA_RESP_ERR__BEGIN";
        //此处省略.....
    }
}


//2.若errnox错误码在我们列举出的enum中,则返回enum中的错误码打印
rd_kafka_resp_err_t rd_kafka_errno2err (int errnox);

一般在函数调用失败的地方的日志打印中,加入上面2个函数:先rd_kafka_errno2err,再rd_kafka_err2str

如:

#include <iostream>
#include <map>
#include <vector>
using namespace std;

enum
{
    OPERATOR_OK = 0,       //成功
    OPERATOR_ERR = 1,      //失败
    MALLOC_MEMORY_ERR = 2, //申请内存空间失败
    FUNCTION_PARAM_NULL = 3,//函数参数为空
    FUNCTION_PARAM_ERR = 4 //函数参数错误
    //省略......
};


/**@fn            Div
 * @brief         除法函数
 * @param[in]     const int a 被除数
 * @param[in]     const int b 除数
 * @param[out]    int &outData 结果值
 * @return        int
**/
int Div(const int a,const int b, int &outData)
{
    if(b <= 0)
    {
        fprintf(stderr,"Div param is err.");
        return FUNCTION_PARAM_ERR;
    }
    outData = a/b;
    return OPERATOR_OK;
}


/**@fn            err2str
 * @brief         将错误码转换为对应字符串
 * @param[out]    const int ierrno 待转换的错误码
 * @return        转换后的错误码字符串
**/
const char* err2str(const int ierrno)
{
    if(OPERATOR_OK >ierrno || ierrno > FUNCTION_PARAM_ERR)
    {
        puts("err2str param is err.");
        return NULL;
    }
    switch (ierrno) {
    case OPERATOR_OK:return "OPERATOR_OK";break;
    case OPERATOR_ERR:return "OPERATOR_ERR";break;
    case MALLOC_MEMORY_ERR:return "MALLOC_MEMORY_ERR";break;
    case FUNCTION_PARAM_NULL:return "FUNCTION_PARAM_NULL";break;
    case FUNCTION_PARAM_ERR:return "FUNCTION_PARAM_ERR";break;
    default:return NULL;break;
    }
}

int main()
{
    int a = 6, b = 2;
    int iResult = 0;
    if(OPERATOR_OK != Div(a,b,iResult))
    {
        printf("Div failed[%d]-[%s]\n",Div(a,b,iResult), err2str(Div(a,b,iResult)));
        return -1;
    }

    std::cout<<"Div: "<<iResult<<std::endl;
    return 0;
}

打印结果

Div:  3

你可能感兴趣的:(C语言)