Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 使用'有什么好处吗;转到';在一种支持循环和函数的语言中?若然,原因为何?_C_Exception Handling_Language Agnostic_Goto - Fatal编程技术网

C 使用'有什么好处吗;转到';在一种支持循环和函数的语言中?若然,原因为何?

C 使用'有什么好处吗;转到';在一种支持循环和函数的语言中?若然,原因为何?,c,exception-handling,language-agnostic,goto,C,Exception Handling,Language Agnostic,Goto,我长期以来的印象是,如果可能的话,不应该使用goto。前几天在阅读libavcodec(用C编写)时,我注意到它有多种用途。在一种支持循环和函数的语言中使用goto是否有利?若然,原因为何 在Perl中,使用标签从循环中“转到”——使用“last”语句,类似于break 这样可以更好地控制嵌套循环 传统的goto标签也受支持,但我不确定是否有太多的情况下,这是实现您想要的唯一方法-子程序和循环应该足以满足大多数情况。goto的问题以及“goto less programming”运动的最重要参数

我长期以来的印象是,如果可能的话,不应该使用
goto
。前几天在阅读libavcodec(用C编写)时,我注意到它有多种用途。在一种支持循环和函数的语言中使用
goto
是否有利?若然,原因为何

在Perl中,使用标签从循环中“转到”——使用“last”语句,类似于break

这样可以更好地控制嵌套循环


传统的goto标签也受支持,但我不确定是否有太多的情况下,这是实现您想要的唯一方法-子程序和循环应该足以满足大多数情况。

goto的问题以及“goto less programming”运动的最重要参数是,如果你太频繁地使用它,你的代码,尽管它可能表现正确,但会变得不可读、不可维护、不可查看等。在99.99%的情况下,“goto”会导致意大利面代码。就我个人而言,我想不出任何好的理由来解释为什么我会使用“goto”。

我们使用的goto规则是,goto可以向前跳到函数中的单个出口清理点。在真正复杂的函数中,我们放宽了这个规则,允许其他函数向前跳。在这两种情况下,我们都避免了错误代码检查时经常出现的深度嵌套的if语句,这有助于可读性和维护。

因为
goto
使程序流的推理变得困难1(也称为“意大利面代码”),
goto
通常仅用于补偿缺少的功能:使用
goto
实际上可能是可以接受的,但前提是该语言不提供更结构化的变体来实现相同的目标。以怀疑为例:


我们使用的goto规则是,对于向前跳到函数中的单个出口清理点,goto是可以接受的

这是正确的–但只有当语言不允许使用清理代码(如RAII或
finally
)进行结构化异常处理时,它才能更好地完成相同的工作(因为它是专门为执行此任务而构建的),或者当有充分的理由不使用结构化异常处理时(但你永远不会有这种情况,除非在一个非常低的水平)

在大多数其他语言中,
goto
唯一可接受的用法是退出嵌套循环。即使在嵌套循环中,将外部循环提升到自己的方法中并使用
return
也几乎总是更好

除此之外,
goto
是一个信号,表明没有足够的思想进入特定的代码段


1支持
goto
的现代语言实施了一些限制(例如
goto
可能不会跳入或跳出函数),但问题基本上是一样的


顺便说一句,其他语言功能当然也是如此,最明显的是例外。通常有严格的规则只在指定的地方使用这些功能,例如不使用例外来控制非例外程序流。

使用“goto”有几个原因我知道的声明(有些人已经说过了):

干净地退出功能

通常在函数中,您可能会分配资源并需要在多个位置退出。程序员可以通过将资源清理代码放在函数末尾来简化代码,函数的所有“退出点”都会转到清理标签。这样,您就不必在函数的每个“退出点”都编写清理代码

退出嵌套循环

如果您在一个嵌套的循环中,并且需要中断所有的循环,那么goto可以使这比break语句和If检查更干净、更简单

低水平性能改进

这只在性能关键的代码中有效,但goto语句执行速度非常快,在通过函数时可以给您带来提升。然而,这是一把双刃剑,因为编译器通常无法优化包含goto的代码

请注意,在所有这些示例中,GOTO仅限于单个函数的范围。

C#语句中。So用于将控制转移到特定的开关盒标签或标签

例如:

switch(value)
{
  case 0:
    Console.Writeln("In case 0");
    goto case 1;
  case 1:
    Console.Writeln("In case 1");
    goto case 2;
  case 2:
    Console.Writeln("In case 2");
    goto default;
  default:
    Console.Writeln("In default");
    break;
}
编辑:“无漏失”规则有一个例外。如果案例语句没有代码,则允许漏失

若然,原因为何

C没有多级/标记中断,并且不是所有的控制流都可以用C的迭代和决策原语轻松建模。gotos在纠正这些缺陷方面走了很长的路

有时,使用某种标志变量来实现一种伪多级中断更为明显,但它并不总是优于goto(至少goto允许用户轻松确定控件的位置,这与标志变量不同),有时你只是不想为避免跳槽而支付旗帜/其他扭曲动作的表演费用


libavcodec是一段性能敏感的代码。控制流的直接表达可能是一个优先事项,因为它会运行得更好。

goto不好的原因之一,除了编码风格外,还可以使用它创建重叠但非嵌套的循环:

这将创建奇怪但可能合法的控制结构流,其中可能存在(a、b、c、b、a、b、a、b等)序列,这使编译器黑客不高兴。显然,有许多聪明的优化技巧依赖于这种类型的结构而不发生。(我应该检查我的龙书副本…)这样做的结果(使用一些编译器)可能是没有对包含
goto
s的代码进行其他优化

如果你只知道“哦,顺便说一句”能说服编译器发出更快的代码,这可能会很有用,
loop1:
  a
loop2:
  b
  if(cond1) goto loop1
  c
  if(cond2) goto loop2
sub factorial {
    my ($n, $acc) = (@_, 1);
    return $acc if $n < 1;
    @_ = ($n - 1, $acc * $n);
    goto &factorial;
}
// Overwrite an element with same hash key if it exists
for (add_index=0; add_index < ELEMENTS_PER_BUCKET; add_index++)
  if (slot_p[add_index].hash_key == hash_key)
    goto add;

// Otherwise, find first empty element
for (add_index=0; add_index < ELEMENTS_PER_BUCKET; add_index++)
  if ((slot_p[add_index].type == TT_ELEMENT_EMPTY)
    goto add;

// Additional passes go here...

add:
// element is written to the hash table here
// Overwrite an element with same hash key if it exists
for (add_index=0; add_index < ELEMENTS_PER_BUCKET; add_index++)
  if (slot_p[add_index].hash_key == hash_key)
    break;

if (add_index >= ELEMENTS_PER_BUCKET) {
  // Otherwise, find first empty element
  for (add_index=0; add_index < ELEMENTS_PER_BUCKET; add_index++)
    if ((slot_p[add_index].type == TT_ELEMENT_EMPTY)
      break;
  if (add_index >= ELEMENTS_PER_BUCKET)
   // Additional passes go here (nested further)...
}

// element is written to the hash table here
    // 1
    try{
      ...
      throw NoErrorException;
      ...
    } catch (const NoErrorException& noe){
      // This is the worst
    } 


    // 2
    do {
      ...break; 
      ...break;
    } while (false);


    // 3
    for(int i = 0;...) { 
      bool restartOuter = false;
      for (int j = 0;...) {
        if (...)
          restartOuter = true;
      if (restartOuter) {
        i = -1;
      }
    }

etc
etc
#define _ -F<00||--F-OO--;
int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO()
{
            _-_-_-_
       _-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
        _-_-_-_-_-_-_-_
            _-_-_-_
}
a[900];     b;c;d=1     ;e=1;f;     g;h;O;      main(k,
l)char*     *l;{g=      atoi(*      ++l);       for(k=
0;k*k<      g;b=k       ++>>1)      ;for(h=     0;h*h<=
g;++h);     --h;c=(     (h+=g>h     *(h+1))     -1)>>1;
while(d     <=g){       ++O;for     (f=0;f<     O&&d<=g
;++f)a[     b<<5|c]     =d++,b+=    e;for(      f=0;f<O
&&d<=g;     ++f)a[b     <<5|c]=     d++,c+=     e;e= -e
;}for(c     =0;c<h;     ++c){       for(b=0     ;b<k;++
b){if(b     <k/2)a[     b<<5|c]     ^=a[(k      -(b+1))
<<5|c]^=    a[b<<5      |c]^=a[     (k-(b+1     ))<<5|c]
;printf(    a[b<<5|c    ]?"%-4d"    :"    "     ,a[b<<5
|c]);}      putchar(    '\n');}}    /*Mike      Laman*/
for (stepfailed=0 ; ! stepfailed ; /*empty*/)
If A <> 0 Then A = 0 EndIf
Write("Value of A:" + A)
If A == 0 Then GOTO FINAL EndIf
   A = 0
FINAL:
Write("Value of A:" + A)
int doSomething (struct my_complicated_stuff *ctx)    
{
    db_conn *conn;
    RSA *key;
    char *temp_data;
    conn = db_connect();  


    if (ctx->smth->needs_alloc) {
      temp_data=malloc(ctx->some_size);
      if (!temp_data) {
        db_disconnect(conn);
        return -1;      
        }
    }

    ...

    if (!ctx->smth->needs_to_be_processed) {
        free(temp_data);    
        db_disconnect(conn);    
        return -2;
    }

    pthread_mutex_lock(ctx->mutex);

    if (ctx->some_other_thing->error) {
        pthread_mutex_unlock(ctx->mutex);
        free(temp_data);
        db_disconnect(conn);        
        return -3;  
    }

    ...

    key=rsa_load_key(....);

    ...

    if (ctx->something_else->error) {
         rsa_free(key); 
         pthread_mutex_unlock(ctx->mutex);
         free(temp_data);
         db_disconnect(conn);       
         return -4;  
    }

    if (ctx->something_else->additional_check) {
         rsa_free(key); 
         pthread_mutex_unlock(ctx->mutex);
         free(temp_data);
         db_disconnect(conn);       
         return -5;  
    }


    pthread_mutex_unlock(ctx->mutex);
    free(temp_data);    
    db_disconnect(conn);    
    return 0;     
}
int doSomething_goto (struct my_complicated_stuff *ctx)
{
    int ret=0;
    db_conn *conn;
    RSA *key;
    char *temp_data;
    conn = db_connect();  


    if (ctx->smth->needs_alloc) {
      temp_data=malloc(ctx->some_size);
      if (!temp_data) {
            ret=-1;
           goto exit_db;   
          }
    }

    ...

    if (!ctx->smth->needs_to_be_processed) {
        ret=-2;
        goto exit_freetmp;      
    }

    pthread_mutex_lock(ctx->mutex);

    if (ctx->some_other_thing->error) {
        ret=-3;
        goto exit;  
    }

    ...

    key=rsa_load_key(....);

    ...

    if (ctx->something_else->error) {
        ret=-4;
        goto exit_freekey; 
    }

    if (ctx->something_else->additional_check) {
        ret=-5;
        goto exit_freekey;  
    }

exit_freekey:
    rsa_free(key);
exit:    
    pthread_mutex_unlock(ctx->mutex);
exit_freetmp:
    free(temp_data);        
exit_db:
    db_disconnect(conn);    
    return ret;     
}
int big_function()
{
    /* do some work */
    if([error])
    {
        /* clean up*/
        return [error];
    }
    /* do some more work */
    if([error])
    {
        /* clean up*/
        return [error];
    }
    /* do some more work */
    if([error])
    {
        /* clean up*/
        return [error];
    }
    /* do some more work */
    if([error])
    {
        /* clean up*/
        return [error];
    }
    /* clean up*/
    return [success];
}
int big_function()
{
    int ret_val = [success];
    /* do some work */
    if([error])
    {
        ret_val = [error];
        goto end;
    }
    /* do some more work */
    if([error])
    {
        ret_val = [error];
        goto end;
    }
    /* do some more work */
    if([error])
    {
        ret_val = [error];
        goto end;
    }
    /* do some more work */
    if([error])
    {
        ret_val = [error];
        goto end;
    }
end:
    /* clean up*/
    return ret_val;
}
if (something())
  goto fail;
  goto fail; // copypasta bug
printf("Never reached\n");
fail:
  // control jumps here
struct Fail {};

try {
  if (something())
    throw Fail();
    throw Fail(); // copypasta bug
  printf("Never reached\n");
}
catch (Fail&) {
  // control jumps here
}
int computation1() {
  return 1;
}

int computation2() {
  return computation1();
}
void tough1() {
  if (computation1() != computation2())
    printf("Unreachable\n");
}

void tough2() {
  if (computation1() == computation2())
    goto out;
  printf("Unreachable\n");
out:;
}

struct Out{};

void tough3() {
  try {
    if (computation1() == computation2())
      throw Out();
    printf("Unreachable\n");
  }
  catch (Out&) {
  }
}
int i;

PROMPT_INSERT_NUMBER:
  std::cout << "insert number: ";
  std::cin >> i;
  if(std::cin.fail()) {
    std::cin.clear();
    std::cin.ignore(1000,'\n');
    goto PROMPT_INSERT_NUMBER;          
  }

std::cout << "your number is " << i;
int i;

bool loop;
do {
  loop = false;
  std::cout << "insert number: ";
  std::cin >> i;
  if(std::cin.fail()) {
    std::cin.clear();
    std::cin.ignore(1000,'\n');
    loop = true;          
  }
} while(loop);

std::cout << "your number is " << i;
void sort(int* array, int length) {
SORT:
  for(int i=0; i<length-1; ++i) if(array[i]>array[i+1]) {
    swap(data[i], data[i+1]);
    goto SORT; // it is very easy to understand this code, right?
  }
}
void sort(int* array, int length) {
  bool seemslegit;
  do {
    seemslegit = true;
    for(int i=0; i<length-1; ++i) if(array[i]>array[i+1]) {
      swap(data[i], data[i+1]);
      seemslegit = false;
    }
  } while(!seemslegit);
}
void sort(int* array, int length) {
  for(int i=0; i<length-1; ++i) if(array[i]>array[i+1]) {
    swap(data[i], data[i+1]);
    i = -1; // it works, but WTF on the first glance
  }
}
; P1 states loops
; 11111110 <-
; 11111101  |
; 11111011  |
; 11110111  |
; 11101111  |
; 11011111  |
; |_________|

init_roll_state:
    MOV P1,#11111110b
    ACALL delay
next_roll_state:
    MOV A,P1
    RL A
    MOV P1,A
    ACALL delay
    JNB P1.5, init_roll_state
    SJMP next_roll_state
if(valid) {
  do { // while(loop)

// more than one page of code here
// so it is better to comment the meaning
// of the corresponding curly bracket

  } while(loop);
} // if(valid)
if(!valid) goto NOTVALID;
  LOOPBACK:

// more than one page of code here

  if(loop) goto LOOPBACK;
NOTVALID:;
for cur_char, next_char in sliding_window(input_string) {
    if cur_char == '%' {
        if next_char == '%' {
            cur_char_index += 1
            goto handle_literal
        }
        # Some additional logic
        if chars_should_be_handled_literally() {
            goto handle_literal
        }
        # Handle the format
    }
    # some other control characters
    else {
      handle_literal:
        # Complicated logic here
        # Maybe it's writing to an array for some OpenGL calls later or something,
        # all while modifying a bunch of local variables declared outside the loop
    }
}
switch( x ) {
    
    case 1: case1() ; doStuffFor123() ; break ;
    case 2: case2() ; doStuffFor123() ; break ;
    case 3: case3() ; doStuffFor123() ; break ;
    
    case 4: case4() ; doStuffFor456() ; break ;
    case 5: case5() ; doStuffFor456() ; break ;
    case 6: case6() ; doStuffFor456() ; break ;
    
    case 7: case7() ; doStuffFor789() ; break ;
    case 8: case8() ; doStuffFor789() ; break ;
    case 9: case9() ; doStuffFor789() ; break ;
}
switch( x ) {
    
    case 1: case1() ; goto stuff123 ;
    case 2: case2() ; goto stuff123 ;
    case 3: case3() ; goto stuff123 ;
    
    case 4: case4() ; goto stuff456 ;
    case 5: case5() ; goto stuff456 ;
    case 6: case6() ; goto stuff456 ;
    
    case 7: case7() ; goto stuff789 ;
    case 8: case8() ; goto stuff789 ;
    case 9: case9() ; goto stuff789 ;
    
    stuff123: doStuffFor123() ; break ;
    stuff456: doStuffFor456() ; break ;
    stuff789: doStuffFor789() ; break ;
}