Puzzle over “goto” goto背后发生了什么

Puzzle over “goto”

 

      很语言都支持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;
}






jason 和 eric都是label。作为goto的对象(目的地貌似更加确切)。 goto将程序执行流直接从goto语句部分,直接跳转到label标识的语句。

 

上面的语句将直接跳转到eric部分,程序最后执行的结果a是4;

 

其实还是很“简单的”

 

问题来了:

上面那个代码里面的

static void *jt[7] = {
&&loc_A, &&loc_def, &&loc_B,
&&loc_C, &&loc_D, &&loc_def,
&&loc_D
};



          loc_A , loc_def,loc_B...都是label,为嘛能取地址?不是变量也可以取地址,而且这里还是两次取地址?

无比的纠结。这个goto的label究竟是怎么回事。。。goto的label还能这样。。。(水平不行)



 
下面这段话是GNU给出的对于label相关操作的解释

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.) 




结论:

&&这个东东 叫做  Label value operator &&


     其实可以作为一个操作符(operator), 当&&右操作数(right operand)是一个label的时候,&&label得到这个label的地址
     当&&左右两边都有操作数的时候,&&是“条件与”  
     当只有一个&时,只有一个右操作数时,作取地址符号。当左右两边是变量名的时候,是“逻辑与”


      值得一提的是汇编里面的label和C语言里面的label有一定的对应性(因为不完全对应,可能原来没有的label,在汇编里面会为了保证程序逻辑的一致性被生成)

标号有如下的属性:

①段基值属性:指标号后面第一条指令所在的代码段的段基值;

②偏移地址属性:指标号后面第一条指令首字节的段内偏移地址;

③类型属性:也称距离属性,是指标号与引用该标号的指令之间允许距离的远、近





对于&&作用于label,我们可以测试

<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
label--jason value:4195682
label--eric value:4195692

相差了10

label标记的是后面指令的地址,jason和eric之间有5个指令(每个指令2个字节)
 

两个nop之间10个字节的说。。。

这里不管运行多少次a.out都是这个结果,原因是虚拟内存技术。真正映射到物理内存上不一定是同一个地址








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单词搜索迷宫(Word Search Puzzle)问题是一个经典的算法问题,其输入是一个二维的字符数组和一组单词,目标是找出字符数组网格中的所有单词。这些单词可以是水平的、垂直的或者是任意的对角线方向,所以需要查找8个不同的方向。解决这个问题的一种常见方法是使用回溯算法,具体步骤如下: 1. 遍历二维字符数组,对于每个字符,以其为起点开始搜索,搜索的方向包括水平、垂直和对角线方向。 2. 对于每个搜索到的单词,将其记录下来。 3. 重复步骤1和2,直到遍历完整个二维字符数组。 下面是一个使用C#语言实现的单词搜索迷宫算法的示例代码: ```csharp class WordSearchPuzzle { private char[,] grid; private HashSet<string> words; public WordSearchPuzzle(char[,] grid, HashSet<string> words) { this.grid = grid; this.words = words; } public void Solve() { int rows = grid.GetLength(0); int cols = grid.GetLength(1); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { Search(i, j, new StringBuilder()); } } } private void Search(int row, int col, StringBuilder sb) { if (row < 0 || row >= grid.GetLength(0) || col < 0 || col >= grid.GetLength(1)) { return; } sb.Append(grid[row, col]); string word = sb.ToString(); if (words.Contains(word)) { Console.WriteLine("Found '{0}' at [{1}, {2}] to [{3}, {4}]", word, row, col, row - sb.Length + 1, col - sb.Length + 1); } if (word.Length < 3) { Search(row + 1, col, sb); Search(row - 1, col, sb); Search(row, col + 1, sb); Search(row, col - 1, sb); Search(row + 1, col + 1, sb); Search(row - 1, col - 1, sb); Search(row + 1, col - 1, sb); Search(row - 1, col + 1, sb); } sb.Remove(sb.Length - 1, 1); } } // 使用示例 char[,] grid = new char[,] { {'t', 'h', 'i', 's'}, {'w', 'a', 't', 's'}, {'o', 'a', 'h', 'g'}, {'f', 'g', 'd', 't'} }; HashSet<string> words = new HashSet<string>() { "this", "two", "fat", "that" }; WordSearchPuzzle puzzle = new WordSearchPuzzle(grid, words); puzzle.Solve(); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值