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