Dynamic label transition in C

I would like to display the output - numbers from 1 to 5, and then 4-5 endlessly. Is there a way to pass the value i (4) instead of the i character in goto1. Or there is some other effective way to implement this without illustrating all the parameters, as in a switch (for example, 1: goto1 (c1), etc.).

The main goal is to move to an expression whose label is calculated in 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); } 
+4
source share
5 answers

If you are ... adventurous (or I mean stupid?), You can use the GCC Shortcuts extension as values .

6.3 Labels as values

You can get the address of the label defined in the current function (or containing the function) with the unary operator ' && . The value is of type void * . This value is a constant and can be used wherever a constant of this type acts. For instance:

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

To use these values, you must be able to switch to one. This is done with the computed operation goto 1 goto *exp; . For instance,

  goto *ptr; 

Any expression of type void * allowed.

One way to use these constants is to initialize a static array, which serves as a jump table:

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

Then you can select a label with indexing, for example:

  goto *array[i]; 

Note that this does not check if the index is indexed in the array indexing method in C, never does it.

Such an array of label values ​​serves a purpose similar to that of the switch statement. The switch statement is cleaner, so use it, not an array, if the problem is not very good for the switch statement.

Another use of label values ​​in the interpreter is for multi-threaded code. Labels inside the interpreter function can be stored in stream code for ultra-fast dispatch.

You cannot use this mechanism to jump to code in another function. If you do this, completely unpredictable things will happen. The best way to avoid this is to save the label address in automatic variables only and never pass it as an argument.

An alternative way to write the above example is

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

This is more convenient for code living in shared libraries, because it reduces the number of necessary dynamic movements and, therefore, allows data to be read-only.

The &&foo expressions for the same label can have different meanings if the containing function is inline or cloned. If the program relies on the fact that they are always the same, __attribute__((__noinline__, __noclone__)) should be used to prevent embedding and cloning. If &&foo used in a static variable initializer, embedding and cloning is prohibited.


Footnotes

[1] A similar function in Fortran is called the assigned goto, but that name seems unacceptable in C, where you can do more than just store label addresses in label variables.

Under no circumstances should this be taken as a recommendation to use this feature. The calculated goto been removed from Fortran; it’s better to leave it in the history bin.

+8
source

Are you asking for a jump table? If you use gcc: it has a transition 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; } 

About this method is that you release the big switch statement.

+5
source

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; } 
+3
source

Since you want to do it wrong (aka creative), do you think a trampoline?

 #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 the idea of ​​using function pointers (see what I did there?), You can 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; } 
+1
source

Will the switch do 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; } 
+1
source

All Articles