我想我明白了,我查看了关于这个主题的所有不同主题,我开始同意那里没有 ansi C 解决方案,但我找到了一种满足我需求的方法。我在 stackoverflow 上看到的所有解决方案都基于“获取”标签的地址,然后将其填充到表中,然后索引该表并转到,这两者都具有 gcc/clang 非 ansi 扩展或 asm 扩展。
我又试了一次,得到了这个。
在一个名为 cgoto.h 的包含文件中,我有这个
#ifndef CGOTO_dcl
#define CGOTO_dcl(N) int CGOTO_##N
#define CGOTO_LE(l) l,
#define CGOTO_LG(l) case l:goto l;
#define CGOTO_def(N) \
if(0){typedef enum {N(CGOTO_LE)} N; CGOTO_##N: switch(CGOTO_##N)\
{N(CGOTO_LG) default:CGOTO_##N=0;goto CGOTO_##N;}}
#define CGOTO(N,i) CGOTO_##N=i; goto CGOTO_##N;
#endif
用法是这样的
#include <stdio.h>
#include "cgoto.h"
int f(int x)
{ //...
CGOTO_dcl(gtb);
//...
# define gtb(L) L(l0) L(l1) L(l2)
CGOTO_def(gtb);
//...
CGOTO(gtb,x);
l0: printf("error\n");
return(0);
//...
l1:return(11);
l2:return(22);
l3:return(33);
}
int main()
{ printf("f(0)=%d f(1)=%d f(2)=%d,f(3)=%d\n",f(0),f(1),f(2),f(3));
}
在这个实现中,跳转的成本是 2 次跳转和一个 switch() 是顺序的,然后是可优化的。因此,与函数调用相比,这是合理的性能,比 &&label 解决方案的性能稍差,但以可移植性为代价。
通过这种实现,标签代码(语义动作)不再局限于 switch(),因此我们可以实现具有共享语义动作的跳转表。
索引分配给本地 goto_table_index,使函数使用此可重入(多线程),尽管优化器可以完全删除此临时分配。
跳转表中的第一个标签是“特殊的”(在此实现中),因为它捕获索引越界,第一个标签是“错误”标签。如果您的代码是防弹的,即您无法获得越界索引,则第一个标签没有特定的语义。
CGOTO_dcl(gtb);
将跳转表 'gtb' 自己的索引声明为可重入的自动整数。
# define gtb(L) L(l0) L(l1) L(l2)
CGOTO_def(gtb);
定义一个名为 gtb 的跳转表,可以用 L(label) 输入/删除标签,非常方便,这本质上是象征性的,即标签是有含义的名称。使用 #define 作为 switch() 情况,添加/抑制标签通常意味着 #define 重新编号是一个问题。
#define 可以与 CGOTO_def() 分开,但将它们放在一起更有意义。 CGOTO_def() 必须放在函数局部声明之后,因为它包含一个 switch() 代码。
一个uniq跳转表可以在函数的多个地方使用。
CGOTO(gtb,x);
...
CGOTO(gtb,y);
一个标签可以进入多个跳转表
# define gtb1(L) L(l0) L(l1) L(l2)
CGOTO_def(gtb1);
# define gtb2(L) L(l0) L(l4) L(l5)
CGOTO_def(gtb2);
总而言之,这可能看起来很丑陋,但是,跳转表定义通过 2 行 #define 和 CGOTO_def() 是可管理且实用的、半高性能且可移植的。
我们回到 FTN4 :)
干杯,
飞