以下是声明大量常量的代码:
#define TD_DATA_PID 0 /* control pipe state for td_sie_done */
#define TD_STATUS_PID 2 /* control pipe state for td_sie_done */
#define TD_DONE_PID 4 /* control pipe state for td_sie_done */
#define TD_DELETE_PID 0xee /* pipe command for td_sie_done */
#define TD_SETUP_PID 0xd /* also used for control pipe state for tf_sie_done */
#define TD_IN_PID 0x9
#define TD_OUT_PID 0x1
#define TD_SOF_PID 0x5
#define TD_PREAMBLE_PID 0xc
#define TD_NAK_PID 0xa
#define TD_STALL_PID 0xe
#define TD_DATA0_PID 0x3
#define TD_DATA1_PID 0xb
#define TD_NORMAL_TIMEOUT 0
#define TD_LONG_TIMEOUT 1
enum TD_CTRL_BITS
{
TD_CTRL_ARM = 0x01,
TD_CTRL_ISOCH = 0x10,
TD_CTRL_SYNC_SOF = 0x20,
TD_CTRL_DTOGGLE = 0x40,
TD_CTRL_PREAMBLE = 0x80
};
enum TD_STATUS_BITS
{
TD_STATUS_ACK = 0x01,
TD_STATUS_ERROR = 0x02,
TD_STATUS_TIMEOUT = 0x04,
TD_STATUS_SEQ = 0x08,
TD_STATUS_SETUP = 0x10,
TD_STATUS_OVERFLOW = 0x20,
TD_STATUS_NAK = 0x40,
TD_STATUS_STALL = 0x80
};
此代码适用于USB OTG控制器,低级嵌入式系统,因此使用C而不是C ++。
所有#define值都用于为单个一个字节变量赋值。变量是数据包ID,因此所有#define常量都以_PID结尾。这很清楚。
然而,枚举部分尚不清楚。
基本上,有一个单字节长的寄存器包含TD_CONTROL_BITS,可能想要读/写它。每个位都会影响硬件的行为。给予枚举内每个常量的值是对应于该函数的位,例如ARM位是位0,同步传输位是位2 e.t.c.该寄存器被读取并写入。
类似也适用于TD_STATUS_BITS。字节长状态寄存器中的每个位提供有关传输状态的独立信息。因此,常数TD_STATUS_ACK,TD_STATUS_ERROR e.t.c可以用作位掩码来读取状态寄存器。该寄存器仅在外部硬件更新时回读,并且只写入0值才能复位。
现在我的问题是,为什么不将#define常量用于一切?在这种情况下,枚举会带来什么好处?此外,在TD_CTRL_BITS的情况下,我们可能想要逻辑OR位一起,例如TD_CTRL_ARM | TD_CTRL_DTOGGLE。我不认为枚举允许这样做。
答案 0 :(得分:2)
这是一个非常标准的编码惯例,你会在职业生涯中多次看到它。
使用枚举是一种关联一组标志的方法,这样你就可以告诉它们属于一起。从代码中可以清楚地看出哪些是控制标志,哪些是状态标志,因为它在枚举标签中是这样说的。有些语言甚至强制执行标志类型,以便只有控制标志可以与控制参数一起使用,并且可以与状态标志一起使用。 C#这样做,我不认为C会这样做,C ++可能。
更可读的代码是一件好事,因为它意味着更少的错误和更少的维护开销
答案 1 :(得分:2)
在这种情况下没有明显的区别。您可以使用枚举常量或定义,在这种特定情况下没有明显的对或错。
总的来说,枚举常量对于与硬件相关的编程是危险的,因为它们必须始终是int
类型,它始终是有符号的。在执行按位算术等操作时,应避免使用带符号的操作数。
您定义的整数常量(例如0xee
)也存在同样的问题 - 它们也是带符号的int
类型。但是对于定义,您可以选择后缀整数常量,例如0xeeu
。现在你得到unsigned int
,这样更方便,更安全。
签名int
常量的一些示例会导致错误:
if(~TD_CTRL_ARM > 0)
错误,操作数已变为否定。TD_STATUS_STALL << 24
错误,假设32位为int。代码将数据移入int
的符号位,调用未定义的行为。~TD_CTRL_ARM >> n
错误,您可能会以算术移位或逻辑移位结束。代码是不可移植的,可能会以意想不到的方式运行。uint8_t data; ... if((~TD_CTRL_ARM & data) > 0)
。 Bug,签名的左操作数意味着隐式提升的data
将保持签名类型。即使右操作数被明确声明为unsigned
,结果也可能会变为负数。如果左操作数是unsigned int
,则右操作数也将变为无符号,代码将按预期工作。等等。
建议/良好实践:
enum
用于任何形式的位表示或按位操作。U
/ u
后缀整数常量。stdint.h
。unsigned char
,bool
,unsigned short
,uint8_t
等。由于整数提升,他们可能会更改签名。