8

我想显示输出 - 数字 1 到 5,然后是 4-5 无限。有什么方法可以传递 i(4) 的值而不是 goto1 中的字符 i。或者是否有任何其他有效的方法来实现这一点,而无需像 switch 中那样说明所有选项(即案例 1:goto1(c1) 等)。

主要目的是跳转到在程序中计算其标签的语句。

#define goto1(i) \
goto c##i

int main(){    
    c1 : printf(" num is 1 \n");
    c2 : printf(" num is 2 \n");
    c3 : printf(" num is 3 \n");
    c4 : printf(" num is 4 \n");
    c5 : printf(" num is 5 \n");

    int i=4;
    goto1(i);
}
4

5 回答 5

11

如果您...喜欢冒险(或者我的意思是傻?),您可以使用 GCC 扩展Labels as Values

6.3 作为值的标签

您可以使用一元运算符 '<code>&&' 获取当前函数(或包含函数)中定义的标签的地址。该值具有类型void *。该值是一个常量,可以在该类型的常量有效的任何地方使用。例如:

 void *ptr;
 /* ... */
 ptr = &&foo;

要使用这些值,您需要能够跳转到一个。这是通过计算的 goto 语句1 ,完成的goto *exp;。例如,

 goto *ptr;

任何类型的表达式void *都是允许的。

使用这些常量的一种方法是初始化一个用作跳转表的静态数组:

 static void *array[] = { &&foo, &&bar, &&hack };

然后您可以选择带有索引的标签,如下所示:

 goto *array[i];

请注意,这不会检查下标是否在范围内——C 中的数组索引从不这样做。

这样一个标签值数组的用途很像 switch 语句的用途。switch 语句更简洁,所以使用它而不是数组,除非问题不适合 switch 语句。

标签值的另一个用途是在线程代码的解释器中。解释器函数中的标签可以存储在线程代码中,以实现超快速调度。

您不能使用此机制跳转到不同函数中的代码。如果你这样做,就会发生完全不可预测的事情。避免这种情况的最佳方法是将标签地址仅存储在自动变量中,并且永远不要将其作为参数传递。

编写上述示例的另一种方法是

 static const int array[] = { &&foo - &&foo, &&bar - &&foo,
                              &&hack - &&foo };
 goto *(&&foo + array[i]);

这对共享库中的代码更友好,因为它减少了所需的动态重定位次数,因此允许数据为只读。

&&foo如果包含函数是内联或克隆的,则相同标签的表达式可能具有不同的值。如果一个程序依赖于它们始终相同,__attribute__((__noinline__, __noclone__))则应使用它们来防止内联和克隆。如果&&foo在静态变量初始化器中使用,则禁止内联和克隆。


脚注

[1] Fortran 中的类似功能称为分配的 goto,但该名称在 C 中似乎不合适,在 C 中,人们可以做的不仅仅是将标签地址存储在标签变量中。

在任何情况下都不应将此视为使用该功能的建议。计算goto最终从 Fortran 中删除;最好丢在历史的垃圾箱里。

于 2013-04-01T02:28:54.697 回答
6

你要跳台吗?如果你使用 gcc:它有一个跳转表机制。

#include <stdio.h>

int main()
{
    unsigned char data[] = { 1,2,3,4,5,4,5,0 };
    // data to "iterate" over, must be 0-terminated in this example

    void *jump_table[] = { &&L00, &&L01, &&L02, &&L03, &&L04, &&L05 };
    // you should fill this with all 256 possible values when using bytes as p-code

    unsigned char *p = data;

    begin:
        goto *jump_table[ *p ];

    L00:
        return 0; // end app
    L01:
        printf("num %i\n", (int)*p);
        goto next;
    L02:
        printf("num %i\n", (int)*p);
        goto next;
    L03:
        printf("num %i\n", (int)*p);
        goto next;
    L04:
        printf("num %i\n", (int)*p);
        goto next;
    L05:
        printf("num %i\n", (int)*p);
        goto next;
    L06:
    L07:
    // ...
    LFF:
        goto next;

    next:
        ++p;            // advance the data pointer to the next byte
        goto begin;     // start over

    return 0;
}

这种方法的优点是你省去了大的 switch 语句。

于 2013-03-29T14:12:20.547 回答
3

为什么不这样做呢?

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    printf(" num is 1 \n");
    printf(" num is 2 \n");
    printf(" num is 3 \n");

    for (;;){
        printf(" num is 4 \n");
        printf(" num is 5 \n");
    }

    /* Not reachable, but will silence any compiler warnings about main
     * not returning a value. */
    return EXIT_SUCCESS;
}
于 2013-03-29T11:31:21.343 回答
3

既然你想以错误的方式(又名创意)来做这件事,你有没有考虑过蹦床?

#include <stdio.h>

typedef void (*generic)(void);
typedef generic (*continuation)(void);

generic first(void);
generic second(void);

int main(void) {
    continuation fubar = first;
    for (;;) {
        fubar = (continuation) fubar();
    }
}

generic first(void) {
    printf(" num is 1 \n"
           " num is 2 \n"
           " num is 3 \n");
    return (generic) second;
}

generic second(void) {
    printf(" num is 4 \n"
           " num is 5 \n");
    return (generic) second;
}

继续使用函数指针的想法(看看我在那里做了什么?Giggity!),您可以使用函数指针数组:

#include <stdio.h>

typedef size_t (*function)(size_t);

size_t first(size_t);
size_t second(size_t);

int main(void) {
    function function[] = { first, first, first, first, second };
    size_t index = 0;

    for (;;) {
        index = function[index](index);
    }
}

size_t first(size_t index) {
    printf(" num is %d \n", ++index);
    return index;
}

size_t second(size_t index) {
    printf(" num is %d \n", index+1);
    return index-1;
}
于 2013-03-29T12:04:56.163 回答
2

开关不会完成同样的事情吗?

int main()
{
    int i = 1;
    while (1)
    {
        switch (i)
        {
            case 1:
                printf(" num is 1 \n");
            case 2:
                printf(" num is 2 \n");
            case 3:
                printf(" num is 3 \n");
            case 4:
                printf(" num is 4 \n");
            case 5:
                printf(" num is 5 \n");
            default:
                break;
        }

        // code to calculate i
        i = 4;
        // end code to calculate i
    }
    return 0;
}
于 2013-03-29T17:13:18.670 回答