C - 为什么用 & 而不是 switch/if 来比较常量?

C - Why compare constants with & instead of switch/if?

我正在阅读 Android 的 OpenSL 文档。引用以下文件:http://mobilepearls.com/labs/native-android-api/ndk/docs/opensles/

"Callback handlers should be prepared to be called more or less frequently, to receive additional event types, and should ignore event types that they do not recognize. Callbacks that are configured with an event mask of enabled event types should be prepared to be called with multiple event type bits set simultaneously. Use "&" 来测试每个事件位而不是 switch case。"

查看OpenSL的官方规范(https://www.khronos.org/registry/sles/specs/OpenSL_ES_Specification_1.0.1.pdf),定义了这3个我感兴趣的常量:

SL_PREFETCHSTATUS 

#define SL_PREFETCHSTATUS_UNDERFLOW          ((SLuint32) 0x00000001) 
#define SL_PREFETCHSTATUS_SUFFICIENTDATA     ((SLuint32) 0x00000002) 
#define SL_PREFETCHSTATUS_OVERFLOW           ((SLuint32) 0x00000003)

如果我没理解错的话,上面的引述是用 & 进行比较,像这样:

if(status & SL_PREFETCHSTATUS_UNDERFLOW) doSomething_1();
if(status & SL_PREFETCHSTATUS_SUFFICIENTDATA) doSomething_2();
if(status & SL_PREFETCHSTATUS_OVERFLOW) doSomething_3();

而不是开关盒:

switch(statusCode){

    case(SL_PREFETCHSTATUS_UNDERFLOW): doSomething_1(); break;
    case(SL_PREFETCHSTATUS_SUFFICIENTDATA): doSomething_2(); break;
    case(SL_PREFETCHSTATUS_OVERFLOW): doSomething_3(); break;
}

现在,我不明白为什么会这样。谁能给我解释一下原因?

PS:如果常量定义为2的倍数,像这样:

#define SL_PREFETCHSTATUS_UNDERFLOW          ((SLuint32) 0x00000001) 
#define SL_PREFETCHSTATUS_SUFFICIENTDATA     ((SLuint32) 0x00000002) 
#define SL_PREFETCHSTATUS_OVERFLOW           ((SLuint32) 0x00000004)

这是有道理的,但规范将最后一个常量定义为 0x00000003 而不是 0x00000004,所以我迷路了。

文档说得很清楚:

Callbacks that are configured with an event mask of enabled event types should be prepared to be called with multiple event type bits set simultaneously.

这意味着尽管标志不是按位的,但仍然存在设置多个标志的可能性。设置的其他标志可能不一定是公开定义的,但可能是私有的、保留的或未记录的,但仍然是系统运行所必需的。

...所以使用开关不是一个好主意,因为这样会失败:

SLuint32 statusCode = SL_PRIVATE_RESERVED_INTERNAL_USE_ONLY | SL_PREFETCH_STATUS_UNDERFLOW;
switch( statusCode ) {
    case SL_PREFETCH_STATUS_UNDERFLOW:
        // this code will never be executed
        break;
}

使用&进行按位比较。因此,如果在 ab 中设置了任何位,a & b 将给出非零结果。这与比较值不同(对于给定的非零值 a,有多个 b 值可以匹配)。

该评论参考了事件常量。您正在查看的不是事件常量,而是状态常量。例如,事件常量为:

#define SL_PLAYEVENT_HEADATEND ((SLuint32) 0x00000001)
#define SL_PLAYEVENT_HEADATMARKER ((SLuint32) 0x00000002)
#define SL_PLAYEVENT_HEADATNEWPOS ((SLuint32) 0x00000004)
#define SL_PLAYEVENT_HEADMOVING ((SLuint32) 0x00000008)
#define SL_PLAYEVENT_HEADSTALLED ((SLuint32) 0x00000010)

您可以看到这些是位掩码值,可以合并。由于它们可以组合,您需要比较单个位而不是整个值,以确保您正确匹配您感兴趣的事件。