Jos*_*eek 53 c label pointers goto memory-address
我知道每个人都讨厌搞砸.在我的代码中,由于我考虑过并且很满意的原因,他们提供了一个有效的解决方案(即我不是在寻找"不要那样做"作为答案,我理解你的预订,并理解我为什么使用它们无论如何).
到目前为止,它们一直很棒,但我希望以这样的方式扩展功能,这要求我基本上能够存储指向标签的指针,然后再转到它们.
如果此代码有效,它将代表我需要的功能类型.但它不起作用,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;
}
Run Code Online (Sandbox Code Playgroud)
Mic*_*yan 60
C和C++标准不支持此功能.但是,GNU编译器集合(GCC)包含一个非标准扩展,用于执行本文所述的此操作.基本上,他们添加了一个特殊的运算符"&&",它将标签的地址报告为"void*"类型.有关详细信息,请参阅文章
PS换句话说,在你的例子中只使用"&&"而不是"&",它将适用于GCC.
PPS我知道你不想让我说出来,但无论如何我都会说,......不要那样做!
R S*_*hko 15
你可以用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;
}
Run Code Online (Sandbox Code Playgroud)
out*_*tis 12
根据C99标准§6.8.6,a的语法goto是:
goto identifier ;
因此,即使您可以获取标签的地址,也不能将其与goto一起使用.
您可以将a goto与a 组合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;
}
Run Code Online (Sandbox Code Playgroud)
如果您将此用于除了混淆的C比赛之外的其他任何事情,我会追捕你并伤害你.
Bri*_*ell 10
该switch ... case陈述基本上是计算的goto.它是如何工作的一个很好的例子是奇怪的黑客被称为Duff的设备:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
您无法goto使用此技术从任意位置执行操作,但您可以将整个函数包装在switch基于变量的语句中,然后设置该变量以指示您要去的位置以及goto该switch语句.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
当然,如果你经常这么做,你会想要写一些宏来包装它.
AnT*_*AnT 10
在非常非常古老的C语言版本中(想想恐龙漫游地球的时间),被称为"C参考手册"版本(指的是Dennis Ritchie编写的文档),标签正式有类型"int of int" (奇怪,但却是真的),这意味着你可以声明一个int *变量
int *target;
Run Code Online (Sandbox Code Playgroud)
并将label的地址分配给该变量
target = label; /* where `label` is some label */
Run Code Online (Sandbox Code Playgroud)
稍后您可以将该变量用作goto语句的操作数
goto target; /* jumps to label `label` */
Run Code Online (Sandbox Code Playgroud)
但是,在ANSI C中,此功能被抛弃了.在标准的现代C中,您无法获取标签的地址,也无法进行"参数化" goto.这种行为应该用switch语句,指向函数的指针和其他方法等来模拟.实际上,即使是"C参考手册"本身也说"标签变量通常是一个坏主意; switch语句几乎总是不必要" (参见"14.4标签").
Fab*_*bel 10
我知道这种感觉,然后每个人都说不应该这样做; 它必须要完成.这是怎么做的:
// unsafe: this needs to use asm goto so the compiler knows
// execution might not come out the other side
#define unsafe_jumpto(a) asm("jmp *%0"::"r"(a):)
// target pointer, possible targets
#define jumpto(a, ...) asm goto("jmp *%0" : : "r"(a) : : __VA_ARGS__)
int main (void)
{
int i=1;
void* the_label_pointer;
the_label:
the_label_pointer = &&the_label;
label2:
if( i-- )
jumpto(the_label_pointer, the_label, label2, label3);
label3:
return 0;
}
Run Code Online (Sandbox Code Playgroud)
标签解除引用运算符&&仅适用于gcc.很明显,跳转组件宏需要专门为每个处理器实现(这个处理器适用于32位和64位x86).还要记住,不能保证堆栈状态在同一函数中的两个不同点处是相同的.至少在开启一些优化的情况下,编译器可能会假定某些寄存器在标签后的某个点包含某些值.这些事情很容易搞砸,然后做编译器没想到的疯狂的事情.务必证明阅读已编译的代码.
小智 5
我将注意到,这里描述的功能(包括&&在gcc中)是用于在C中实现Forth语言解释器的IDEAL.这使得所有"不要那样做"的论点脱离了水 - 这种功能与方式之间的契合Forth的内部翻译工作太好了,不容忽视.
您可以使用 C 中的标签执行的唯一官方支持的操作就是goto它。正如您所注意到的,您无法获取它的地址或将其存储在变量或其他任何内容中。因此,我不会说“不要那样做”,而是说“你不能那样做”。
看来您必须找到不同的解决方案。也许是汇编语言,如果这对性能至关重要的话?