C89计算goto(再次)如何

我需要编写一个自动机代码,我碰到了这个计算goto的旧需求(ala fortran4 :))

我需要在便携式ansi-C中对此进行编码。

我想远离“不要那样做”,远离longjmp / setjmp,远离嵌入式ASM(),远离非ansi-C扩展。

有谁知道如何做到这一点?

就像我在评论中说的那样,尽管你请求不使用除了goto以外的任何东西,但是标准C没有提供任何东西。

适当地设计你的状态,并将指针传递给处理程序函数以供它们修改。 这样处理程序可以设置下一个要调用的函数。 像这样的东西:

 struct state; typedef void state_func(struct state*); #define NULL_ACTION_ADDRESS (state_func*)0 struct state { state_func *action; int value1; int value2; }; #define INIT_STATE { initial_action, -1, -1} state_func initial_action; state_func handle_a; state_func handle_b; int main(void) { struct state s = INIT_STATE; while(s.action != NULL_ACTION_ADDRESS) { (*s.action)(&s); } return 0; } void initial_action(struct state* ps) { ps->action = &handle_a; } void handle_a(struct state* ps) { ps->action = &handle_b; } void handle_b(struct state* ps) { ps->action = NULL_ACTION_ADDRESS; } 

我想我得到了它,我回顾了这个主题的所有各种线程,我开始同意那里没有ansi C解决方案,但我找到了一种方法来满足我的需求。 我在stackoverflow上看到的所有解决方案都基于“获取”标签的addr的想法,然后将其填充到表中,然后将此表索引并转到goto,这是gcc / clang非ansi扩展或asm扩展。

我给了它另一个尝试tonite并得到了这个。

在一个名为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  #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)=%df(1)=%df(2)=%d,f(3)=%d\n",f(0),f(1),f(2),f(3)); } 

在此实现中,跳转的成本是2次跳转,并且switch()是顺序的,然后是可优化的。 因此,与function调用相比,这是合理的性能,与便携性相比,性能略低于&&标签解决方案。

通过这种实现,标签代码(语义动作)不会局限于switch(),因此我们可以使用共享语义动作实现跳转表。

索引被分配给本地goto_table_index,使得该函数使用此重入(multithreading),尽管优化器可以完全删除此临时赋值。

跳转表中的第一个标签是“特殊的”(在此实现中),因为它捕获索引超出范围,第一个标签是“错误”标签。 如果您的代码是防弹的,即您无法获得越界索引,则第一个标签没有特定的语义。

 CGOTO_dcl(gtb); 

将跳转表’gtb’自己的索引声明为自动整数,以便重入。

  # define gtb(L) L(l0) L(l1) L(l2) CGOTO_def(gtb); 

定义一个名为gtb的跳转表,可以使用L(标签)输入/删除标签,因此非常方便,这本质上是符号的,即标签是具有含义的名称。 使用#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了:)

干杯,披