我知道每个人都讨厌搞砸。在我的代码中,由于我考虑过并且很满意的原因,他们提供了一个有效的解决方案(即我不是在寻找“不要那样做”作为答案,我理解你的预订,并理解我为什么使用它们无论如何)。
到目前为止,它们一直很精彩,但我想以这样的方式扩展功能,这要求我基本上能够存储指向标签的指针,然后再转到它们。
如果此代码有效,它将代表我需要的功能类型。但它不起作用,30分钟的谷歌搜索没有透露任何东西。有没有人有任何想法?
int main (void)
{
int i=1;
void* the_label_pointer;
the_label:
the_label_pointer = &the_label;
if( i-- )
goto *the_label_pointer;
return 0;
}
答案 0 :(得分:58)
C和C ++标准不支持此功能。但是,GNU编译器集合(GCC)包含一个非标准扩展,用于执行此操作,如this article中所述。基本上,他们增加了一个特殊的运营商“&&&”报告标签的地址为“void *”类型。有关详细信息,请参阅文章。
P.S。换句话说,只需使用“&&”而不是“&”在你的例子中,它将适用于GCC P.P.S.我知道你不想让我这么说,但无论如何我都会说,......不要那样做!
答案 1 :(得分:16)
您可以使用setjmp / longjmp执行类似操作。
int main (void)
{
jmp_buf buf;
int i=1;
// this acts sort of like a dynamic label
setjmp(buf);
if( i-- )
// and this effectively does a goto to the dynamic label
longjmp(buf, 1);
return 0;
}
答案 2 :(得分:12)
根据C99标准§6.8.6,goto
的语法是:
goto identifier ;
因此,即使您可以获取标签的地址,也不能将其与goto一起使用。
您可以将goto
与switch
结合使用,类似于计算goto
,效果类似:
int foo() {
static int i=0;
return i++;
}
int main(void) {
enum {
skip=-1,
run,
jump,
scamper
} label = skip;
#define STATE(lbl) case lbl: puts(#lbl); break
computeGoto:
switch (label) {
case skip: break;
STATE(run);
STATE(jump);
STATE(scamper);
default:
printf("Unknown state: %d\n", label);
exit(0);
}
#undef STATE
label = foo();
goto computeGoto;
}
如果您将此用于除了混淆的C比赛以外的任何其他内容,我会追捕并伤害您。
答案 3 :(得分:9)
switch ... case
语句基本上是computed goto
。它如何运作的一个很好的例子是奇怪的黑客,称为Duff's Device:
send(to, from, count)
register short *to, *from;
register count;
{
register n=(count+7)/8;
switch(count%8){
case 0: do{ *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
}while(--n>0);
}
}
您无法使用此技术从任意位置执行goto
,但您可以根据变量将整个函数包装在switch
语句中,然后设置该变量指示您想要的位置去,goto
转换语句。
int main () {
int label = 0;
dispatch: switch (label) {
case 0:
label = some_computation();
goto dispatch;
case 1:
label = another_computation();
goto dispatch;
case 2:
return 0;
}
}
当然,如果你做了很多,你想写一些宏来包装它。
此技术以及一些便利宏甚至可用于实现coroutines in C。
答案 4 :(得分:9)
在非常非常旧的C语言版本中(想想恐龙漫游地球的时间),被称为“C参考手册”版本(指的是Dennis Ritchie编写的document),正式标注有类型“数组的int”(奇怪但真实),这意味着你可以声明一个int *
变量
int *target;
并将标签的地址分配给该变量
target = label; /* where `label` is some label */
稍后您可以将该变量用作goto
语句
goto target; /* jumps to label `label` */
但是,在ANSI C中,此功能被抛弃了。在标准的现代C中,您无法获取标签的地址,也无法进行“参数化”goto
。这种行为应该用switch
语句,指向函数的指针和其他方法等来模拟。实际上,甚至“C参考手册”本身也说“标签变量一般来说是个坏主意; switch语句使得他们几乎总是没必要“(见"14.4 Labels")。
答案 5 :(得分:8)
我知道这种感觉然后每个人都说不应该这样做;只需要 即可完成。这是如何做到的:
#define jumpto(a) asm("jmp *%0"::"r"(a):)
int main (void)
{
int i=1;
void* the_label_pointer;
the_label:
the_label_pointer = &&the_label;
if( i-- )
jumpto(the_label_pointer);
return 0;
}
标签解除引用运营商&&只适用于gcc。很明显,跳转组件宏需要专门为每个处理器实现(这个处理器适用于32位和64位x86)。还要记住,不能保证堆栈状态在同一函数中的两个不同点处是相同的。至少在开启一些优化的情况下,编译器可能会假定某些寄存器在标签后的某个点包含某些值。这些事情很容易搞砸,然后做编译器没想到的疯狂的事情。务必证明阅读已编译的代码。
答案 6 :(得分:5)
我会注意到这里所描述的功能(包括&& in gcc)是用于在C中实现Forth语言解释器的IDEAL。它将所有“不要那样做”的论点从水中吹出 - 适合在这个功能和Forth的内部翻译工作方式之间太好了,不容忽视。
答案 7 :(得分:4)
使用函数指针和while循环。不要制作一段代码,其他人将不得不后悔为你修理。
我认为你试图以某种方式在外部改变标签的地址。函数指针将起作用。
答案 8 :(得分:3)
你可以用C中的标签做的唯一官方支持的事情是goto
它。正如您所注意到的,您无法获取它的地址或将其存储在变量或其他任何内容中。因此,我不会说“不要那样做”,而是说“你做不到”。
看起来你必须找到一个不同的解决方案。也许汇编语言,如果这是性能关键的?
答案 9 :(得分:2)
#include <stdio.h>
int main(void) {
void *fns[3] = {&&one, &&two, &&three};
char p;
p = -1;
goto start; end: return 0;
start: p++;
goto *fns[p];
one: printf("hello ");
goto start;
two: printf("World. \n");
goto start;
three: goto end;
}
答案 10 :(得分:1)
阅读本文:setjmp.h - Wikipedia如前所述,使用setjmp / longjmp可以将变量存储在变量中并稍后跳回。
答案 11 :(得分:0)
根据this thread,标签点不是标准,因此它们是否起作用取决于您正在使用的编译器。
答案 12 :(得分:0)
您可以使用&amp;&amp;来为变量指定标签。 这是您修改后的代码。
int main (void)
{
int i=1;
void* the_label_pointer = &&the_label;
the_label:
if( i-- )
goto *the_label_pointer;
return 0;
}
答案 13 :(得分:0)
你可以使用指向函数的指针来执行类似Fortran计算机的操作。
//全局变量在这里
void c1(){//代码块
}
void c2(){//代码块
}
void c3(){ //代码块
}
void(* goTo [3])(void)= {c1,c2,c3};
//然后
int x = 0;goTo [x ++]();
goTo [x ++]();
goTo [x ++]();