您的位置:首页 > 编程语言 > Go语言

为什么使用goto语句作错误处理

2018-01-10 14:44 232 查看

2月22日,苹果更新了iOS7.0.6,修复了一个bug,之后引起轩然大波,这个低级的bug会导致严重的安全问题.    这个bug很简单,就是在代码中多写了一条goto fail语句,我们且不讨论这个bug如何(有兴趣的同学可以查阅文章末尾的链接),我们来讨论另一个问题.

    稍微有一些linux驱动编程经验的同学都知道,一般都会使用goto语句作错误处理,上大一的时候教C语言的老师就和我们说过,尽量少使用goto语句,这样会增加程序的不稳定性,使得程序运行难以捉摸,那么为什么这么喜欢使用goto语句作为错误处理呢.

    我们来举个简单例子,我们来看两份代码:一份不带goto的错误处理的;一份带goto的错误处理的.一对比大家就清楚明了.

    先来看一份"普通版"的代码:

[cpp]
view plain

copy

print?

<span style="font-size: 14px;">         
       retval = request_irq(IRQ_EINT(20), buttons_interrupt, IRQF_DISABLED,   
            "KEY1", (void *)EINT_DEVICE_ID);  
    if(retval){  
        err("request eint20 failed");         
        return error;   
    }  
  
    /* Driver register */  
    major = register_chrdev(major, DRIVER_NAME, &key_fops);  
    if(major < 0){  
        err("register char device fail");  
        free_irq(IRQ_EINT(20), (void *)EINT_DEVICE_ID);  
        retval = major;  
        return retval;  
    }  
    key_class=class_create(THIS_MODULE,DRIVER_NAME);  
    if(IS_ERR(key_class)){  
        err("class create failed!");  
        free_irq(IRQ_EINT(20), (void *)EINT_DEVICE_ID);  
        unregister_chrdev(major, DRIVER_NAME);  
        retval =  PTR_ERR(key_class);  
        return retval;  
    }  
    key_device=device_create(key_class,NULL, MKDEV(major, minor), NULL,DRIVER_NAME);  
    if(IS_ERR(key_device)){  
        err("device create failed!");  
        free_irq(IRQ_EINT(20), (void *)EINT_DEVICE_ID);  
        unregister_chrdev(major, DRIVER_NAME);  
        class_destroy(key_class);  
        retval = PTR_ERR(key_device);  
        return error_device;  
    }</span>  

retval = request_irq(IRQ_EINT(20), buttons_interrupt, IRQF_DISABLED,
"KEY1", (void *)EINT_DEVICE_ID);
if(retval){
err("request eint20 failed");
return error;
}

/* Driver register */
major = register_chrdev(major, DRIVER_NAME, &key_fops);
if(major < 0){
err("register char device fail");
free_irq(IRQ_EINT(20), (void *)EINT_DEVICE_ID);
retval = major;
return retval;
}
key_class=class_create(THIS_MODULE,DRIVER_NAME);
if(IS_ERR(key_class)){
err("class create failed!");
free_irq(IRQ_EINT(20), (void *)EINT_DEVICE_ID);
unregister_chrdev(major, DRIVER_NAME);
retval =  PTR_ERR(key_class);
return retval;
}
key_device=device_create(key_class,NULL, MKDEV(major, minor), NULL,DRIVER_NAME);
if(IS_ERR(key_device)){
err("device create failed!");
free_irq(IRQ_EINT(20), (void *)EINT_DEVICE_ID);
unregister_chrdev(major, DRIVER_NAME);
class_destroy(key_class);
retval = PTR_ERR(key_device);
return error_device;
}
      看到了吗.因为每一步的错误处理需要把之前申请或注册成功的资源全部都释放掉,比如class_create失败需要注销irq和驱动(因为它们已经成功了,到这一步失败了,那么之前的成功就没有意义了,所以因为一切要恢复到最初的样子),所以这会产生大量重复的代码,free_irq这个函数写了三次,unregister_chrdev写了二次,那咋办呢?

    请看我们文艺版的代码.

   

[cpp]
view plain

copy

print?

<span style="font-size: 14px;">          
        retval = request_irq(IRQ_EINT(20), buttons_interrupt, IRQF_DISABLED,   
            "KEY1", (void *)EINT_DEVICE_ID);  
    if(retval){  
        err("request eint20 failed");  
        goto error;  
    }  
    /* Driver register */  
    major = register_chrdev(major, DRIVER_NAME, &key_fops);  
    if(major < 0){  
        err("register char device fail");  
        retval = major;  
        goto error_register;  
    }  
    key_class=class_create(THIS_MODULE,DRIVER_NAME);  
    if(IS_ERR(key_class)){  
        err("class create failed!");  
        retval =  PTR_ERR(key_class);  
        goto error_class;  
    }  
    key_device=device_create(key_class,NULL, MKDEV(major, minor), NULL,DRIVER_NAME);  
    if(IS_ERR(key_device)){  
        err("device create failed!");  
        retval = PTR_ERR(key_device);  
        goto error_device;  
    }  
    __debug("register myDriver OK! Major = %d\n", major);  
    return 0;  
  
error_device:  
    class_destroy(key_class);  
error_class:  
    unregister_chrdev(major, DRIVER_NAME);  
error_register:  
    free_irq(IRQ_EINT(20), (void *)EINT_DEVICE_ID);  
error:  
    return retval;  
}  
</span>  

retval = request_irq(IRQ_EINT(20), buttons_interrupt, IRQF_DISABLED,
"KEY1", (void *)EINT_DEVICE_ID);
if(retval){
err("request eint20 failed");
goto error;
}
/* Driver register */
major = register_chrdev(major, DRIVER_NAME, &key_fops);
if(major < 0){
err("register char device fail");
retval = major;
goto error_register;
}
key_class=class_create(THIS_MODULE,DRIVER_NAME);
if(IS_ERR(key_class)){
err("class create failed!");
retval =  PTR_ERR(key_class);
goto error_class;
}
key_device=device_create(key_class,NULL, MKDEV(major, minor), NULL,DRIVER_NAME);
if(IS_ERR(key_device)){
err("device create failed!");
retval = PTR_ERR(key_device);
goto error_device;
}
__debug("register myDriver OK! Major = %d\n", major);
return 0;

error_device:
class_destroy(key_class);
error_class:
unregister_chrdev(major, DRIVER_NAME);
error_register:
free_irq(IRQ_EINT(20), (void *)EINT_DEVICE_ID);
error:
return retval;
}


    有了goto语句了,重复的代码都可以省去了,错误处理的代码只写一次就可以了.每次错误处理只需把上一步的成功的资源给注销就可以了而不是像之前一样需要把之前所有的都注销,这才是真正优雅的代码呀!

    但同时也不要忘了,在这里使用goto,跳转范围都是init函数之内,在把控范围之内,所以可以推荐使用.其它地方想使用的话,还是去回想C语言老师的话吧!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: