很语言都支持goto,C也是,但是关于goto的争论一直没有停止过。
wiki上面写了一段话,关于goto的争论。。。viewer可以看看。
Programming style, like writing style, is somewhat of an art and can not be codified by inflexible rules, although discussions about style often seem to center exclusively around such rules. In the case of the goto statement, it has long been observed that unfettered use of goto's quickly leads to unmaintainable spaghetti code. However, a simple, unthinking ban on the goto statement does not necessarily lead immediately to beautiful programming: an unstructured programmer is just as capable of constructing a Byzantine tangle without using any goto's (perhaps substituting oddly-nested loops and Boolean control variables, instead). Many programmers adopt a moderate stance: goto's are usually to be avoided, but are acceptable in a few well-constrained situations,if necessary: as multi-level break statements, to coalesce common actions inside a switch statement, or to centralize cleanup tasks in a function with several error returns. (...) Blindly avoiding certain constructs or following rules without understanding them can lead to just as many problems as the rules were supposed to avert. Furthermore, many opinions on programming style are just that: opinions. They may be strongly argued and strongly felt, they may be backed up by solid-seeming evidence and arguments, but the opposing opinions may be just as strongly felt, supported, and argued. It's usually futile to get dragged into "style wars", because on certain issues, opponents can never seem to agree, or agree to disagree, or stop arguing.
Back to our theme:
对于goto,我一直是极力避免的,但是今天看汇编的时候遇到了,jmp,进而又遇到一个C关于分支跳转的问题。代码里面的细节部分让我很苦恼(水平不够啊。。)。
让我puzzle的代码:
int switch_eg_impl(int x, int n) { /*Table of code pointers */ static void *jt[7] = { &&loc_A, &&loc_def,&&loc_B, &&loc_C, &&loc_D,&&loc_def, &&loc_D }; unsigned index= n- 100; intresult; if(index > 6) gotoloc_def; /*Multiway branch */ goto*jt[index]; loc_def: /* Default case*/ result = 0; gotodone; loc_C: /* Case 103 */ result = x; gotorest; loc_A:/* Case 100 */ result=x*13; gotodone; loc_B: /* Case 102 */ result=x+10; /*Fall through */ rest: /* Finish case 103 */ result += 11; gotodone; loc_D: /* Cases 104, 106 */ result=x*x; /*Fall through */ done: return result; }
还是先简单的介绍一下goto的用法吧。。。避免viewer布吉岛goto
The syntax for a goto statement in C is as follows:
goto label; .. . label: statement;
Here label can be any plain text except C keyword and it can beset anywhere in the C program above or below to goto statement.
Flow Diagram:
demo:
int main() { int a = 1; int b = 3; if(a == 0) { goto jason; } else { goto eric; } a = 10; jason: b = 20; eric: a = a+b; return 0; }
上面的语句将直接跳转到eric部分,程序最后执行的结果a是4;
其实还是很“简单的”
问题来了:
上面那个代码里面的
static void *jt[7] = { &&loc_A, &&loc_def, &&loc_B, &&loc_C, &&loc_D, &&loc_def, &&loc_D };
5.3 Labels as Values
You can get the address of alabel 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 statement, 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 will serve 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 thesubscript 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 will 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.
同样CSAPP上面也给出了解释
These locations are defined by labels in the code, and indicated in the entries in jtby code pointers, consisting of the labels prefixed by ‘&&.’ (Recall that the operator & creates a pointer for a data value. In making this extension, the authors of gcc created a new operator &&to create a pointer for a code location.)
①段基值属性:指标号后面第一条指令所在的代码段的段基值;
②偏移地址属性:指标号后面第一条指令首字节的段内偏移地址;
③类型属性:也称距离属性,是指标号与引用该标号的指令之间允许距离的远、近
<span style="font-size:14px;">#include <stdio.h> int main() { int a = 1; int b = 3; printf("label--jason value:%d\nlabel--eric value:%d\n",&&jason,&&eric); if(a == 0) { goto jason; } else { goto eric; } a = 10; jason: b = 20; eric: a = a+b; return 0; }</span>liuzjian@ubuntu:~/Desktop$ ./a.out