我正在阅读 GNU C 手册并阅读了有关 goto 语句的部分:
作为扩展,GCC 允许 goto 语句跳转到某个地址 由 void* 变量指定。为了使这项工作有效,您还需要 使用一元运算符 &&(不是 &)获取标签的地址。 这是一个人为的例子:
enum Play { ROCK=0, PAPER=1, SCISSORS=2 }; enum Result { WIN, LOSE, DRAW }; static enum Result turn (void) { const void * const jumptable[] = {&&rock, &&paper, &&scissors}; enum Play opp; /* opponent’s play */ goto *jumptable[select_option (&opp)]; rock: return opp == ROCK ? DRAW : (opp == PAPER ? LOSE : WIN); paper: return opp == ROCK ? WIN : (opp == PAPER ? DRAW : LOSE); scissors: return opp == ROCK ? LOSE : (opp == PAPER ? WIN : DRAW); }
const
中的第二个const void * const jumptable[]
是做什么用的?声明基本上就是array of const pointers to const void
,对吧?但我不明白这意味着什么,抱歉,如果这听起来很愚蠢。
那么,&opp 是什么意思呢?该代码对其地址有何作用? select_option()是什么?
当
const
放置在指针 *
声明的右侧时,它使指针本身变为只读。如果放置在 *
左侧,则指向的数据是只读的。
当设计带有函数指针而不是标签的传统跳转表时,通常将表本身设置为只读。有两个原因:
* const
只读限定符意味着跳转表将分配在非易失性闪存而不是RAM中。这意味着它被分配在真正的只读存储器中,而且还可以节省宝贵的 RAM - 在一般的微控制器编程场景中,RAM 比闪存更有价值。那么,&opp 是什么意思?
我不能确定,因为你没有包含
select_option
函数,但它可能是这种性质的函数:
enum Play select_option (enum Play* p)
{
*p = some_decision();
return *p;
}
也就是说,函数会更改传递的变量,但也会返回它以方便函数调用。而不是用返回类型
void
来声明它,在这种情况下我们必须编写
select_option (&opp);
goto *jumptable[opp];
这无疑不是问题,因为代码更具可读性,并且“单行上的最多子表达式赢得价格”的编程形式是非常糟糕的做法。
一般来说,让一个函数通过多种方式返回相同的值(既作为返回值又作为参数之一)实际上被认为是有问题的 API 设计。但这是另一个故事了。