Dynamic jump to label in C

2020-03-02 04:08发布

I would like to display the output - numbers 1 to 5, followed by 4-5 infinitely. Is there any way i can pass the value of i(4) instead of the character i in goto1. Or is there any other efficient way of realizing this without illustrating all the options as in switch(i.e case 1: goto1(c1) ,etc..).

The main aim is to jump to a statement whose label is computed within the program.

#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);
}

标签: c
5条回答
\"骚年 ilove
2楼-- · 2020-03-02 04:49

Are you asking for a jump table? If you are using gcc: It has a jump table mechanism.

#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;
}

The pro about this method is that you spare the large switch statement.

查看更多
三岁会撩人
3楼-- · 2020-03-02 04:55

If you are ... adventurous (or do I mean silly?), you can use a GCC extension Labels as Values.

6.3 Labels as Values

You can get the address of a label defined in the current function (or a containing function) with the unary operator ‘&&’. The value has type void *. This value is a constant and can be used wherever a constant of that type is valid. For example:

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

To use these values, you need to be able to jump to one. This is done with the computed goto statement1, goto *exp;. For example,

 goto *ptr;

Any expression of type void * is allowed.

One way of using these constants is in initializing a static array that serves as a jump table:

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

Then you can select a label with indexing, like this:

 goto *array[i];

Note that this does not check whether the subscript is in bounds—array indexing in C never does that.

Such an array of label values serves a purpose much like that of the switch statement. The switch statement is cleaner, so use that rather than an array unless the problem does not fit a switch statement very well.

Another use of label values is in an interpreter for threaded code. The labels within the interpreter function can be stored in the threaded code for super-fast dispatching.

You may not use this mechanism to jump to code in a different function. If you do that, totally unpredictable things happen. The best way to avoid this is to store the label address only in automatic variables and never pass it as an argument.

An alternate way to write the above example is

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

This is more friendly to code living in shared libraries, as it reduces the number of dynamic relocations that are needed, and by consequence, allows the data to be read-only.

The &&foo expressions for the same label might have different values if the containing function is inlined or cloned. If a program relies on them being always the same, __attribute__((__noinline__, __noclone__)) should be used to prevent inlining and cloning. If &&foo is used in a static variable initializer, inlining and cloning is forbidden.


Footnotes

[1] The analogous feature in Fortran is called an assigned goto, but that name seems inappropriate in C, where one can do more than simply store label addresses in label variables.

Under no circumstances should this be taken as a recommendation to use the feature. The computed goto was eventually removed from Fortran; it is best left in the dustbin of history.

查看更多
Emotional °昔
4楼-- · 2020-03-02 05:08

Since you want to do this the wrong (aka. creative) way, have you considered trampolining?

#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;
}

Continuing on from the idea of using function pointers (see what I did there? Giggity!), you could use an array of function pointers:

#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;
}
查看更多
狗以群分
5楼-- · 2020-03-02 05:12

Why not do it like this?

#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;
}
查看更多
家丑人穷心不美
6楼-- · 2020-03-02 05:15

Wouldn't a switch accomplish the same thing?

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;
}
查看更多
登录 后发表回答