关于goto的使用

坛子里回答大家问题,讨论到了goto。比较有代表性,这里摘录出来。

删除 回复#7

阿东(C/C++爱好者) 13小时前

张初语(Java学生): vc不是开发环境么,c++是语言.一直以为是一样的呢,没想到还有这种区别啊.
上面的我也会,不过我的"会"肯定和肖老师的会不在同一个字典上.
果然是学得太

肖兄:
4.类中静态数据成员,学了以后发现一直没用过
5.虚函数但是常用,有些特性没虚函数都感觉实现不了
6.从来不用异常,用goto------------------C++是反对用goto的难到正统的C程序不是这样吗。

  • 肖舸

    编辑 删除 #9

    肖舸 12小时前

    阿东(C/C++爱好者): 肖兄:
    4.类中静态数据成员,学了以后发现一直没用过
    5.虚函数但是常用,有些特性没虚函数都感觉实现不了
    6.从来不用异常,用goto------------------C++是反对用goto的难

    呵呵,goto的用法,只要我说一次用goto,就有人反对一次。
    上次开会,我公布我的编程规范,还差点打起来。
    不过最后大家都还是用了,还觉得很好用。
    人云亦云,自古有之,IT业也不能免俗啊。
    事实上,大家想想,从1969年到现在,40年了,C语言没有取消goto语句,C++这么多年,居然也继承下来了,为什么?
    那是因为每条语句一定有其特定的用途。具有不可替代性。
    无数说面向对象编程的书籍,以及将模块化编程的书籍,对goto嗤之以鼻,视乎不批判goto就不足以说明自己的纯粹。
    呵呵,先回到这里,我准备个例子传上来。

  • 肖舸

    编辑 删除 #10

    肖舸 12小时前

    例子一:不用goto,想想需要申请的指针是10个的话,程序怎么写?
    void Func(void)
    {
            char* p1=null;
            char* p2=null;
            char* p3=null;
            p1=(char*)malloc(10);
            if(!p1) return;
            p2=(char*)malloc(10);
            if(!p2)
            {
                    free(p1);
                    p1=null;
                    return;
            }
            p3=(char*)malloc(10);
            if(!p3)
            {
                    free(p1);
                    p1=null;
                    free(p2);
                    p2=null;
                    return;
            }
            //...
            if(p1)
            {
                    free(p1);
                    p1=null;
            }
            if(p2)
            {
                    free(p2);
                    p2=null;
            }
            if(p3)
            {
                    free(p3);
                    p3=null;
            }
    }
    例子二:用goto
    void Func(void)
    {
            char* p1=null;
            char* p2=null;
            char* p3=null;
            p1=(char*)malloc(10);
            if(!p1) goto Func_End_Process;
            p2=(char*)malloc(10);
            if(!p2) goto Func_End_Process;
            p3=(char*)malloc(10);       
            if(!p3) goto Func_End_Process;
            //...
    Func_End_Process:
            if(p1)
            {
                    free(p1);
                    p1=null;
            }
            if(p2)
            {
                    free(p2);
                    p2=null;
            }
            if(p3)
            {
                    free(p3);
                    p3=null;
            }
    }

编辑 删除 #18

肖舸 9小时前

四川大学锦江学院 黄启银(C/C++学生): 就是你上面用的那申请10个指针而不用GOTO语句

这么说吧,程序开发中很多都有二元操作。
malloc-free
new-delete
lock-unlock
。。。
这种操作在实际工程应用中很多。
如果一个函数有多个出口,则在每个出口处,会产生巨大的退出代码,这样一来工作量大,程序员疲于奔命,二来全凭人脑记忆,稍微忘一件事情,就会出现内存泄漏,或者double-lock。
而这些bug,在运行时全部是随机产生,几乎无法寻找。
想想看,明天就要交活,而你还有个内存泄漏没找到,老板说,搞不定明天请你吃鱿鱼,你什么感觉?
又或者,你到客户处做技术维护,整个电信局机房的人都看着你,旁边还有竞争对手在进谗言,说这家公司就这水平,呵呵,你却发现有内存泄漏,系统运行2小时后会挂掉,一头的汗水,什么感觉?
所以我们规定,每个函数只能有一个出口,所有的资源释放必须放在出口统一解决,那么,不用goto,全部使用大括号,十几个,几十个if判断条件下来,你数数你的大括号有多深?根据缩进原则,屏幕宽度还够不?你还看得见你的代码吗?
这种代码可读性好不好?万一写错了,好不好找错误?
所有这些问题,一个goto就解决了。

<think>我们正在讨论goto语句的适用场景。根据用户提供的引用内容,我们可以总结出以下几个主要适用场景: 1. **跳出多层嵌套循环**:在深度嵌套的循环结构中,使用goto可以一次性跳出所有循环,避免使用多个break或设置标志位的复杂性[^2][^3][^5]。 2. **错误处理和资源清理**:在函数中多个位置需要清理资源(如关闭文件、释放内存等)时,goto可以跳转到统一的清理代码段,避免重复代码[^5]。 3. **实现特定逻辑流程**:如引用[4]中的示例,使用goto实现一个简单的循环逻辑(如要求用户输入特定内容)。 同时,引用中也提到了一些注意事项: - goto只能在同一个函数内跳转,不能跨函数跳转[^5]。 - 应避免滥用goto,因为它可能导致代码难以理解和维护。 因此,在回答中,我们将重点介绍这些适用场景,并给出相应的代码示例(如用户提供的跳出多层循环和错误处理的例子),同时强调使用时的注意事项。 回答结构: 1. 开头简要说明goto语句在特定场景下的价值。 2. 分点列出主要适用场景,每个场景配以代码示例和引用来源。 3. 总结使用goto的注意事项。 4. 生成相关问题。 注意:用户提供的引用标记为[^1]到[^5],我们在回答中引用时需对应标注。</think>### Goto语句的适用场景与使用规范 `goto`语句在特定场景下能简化代码结构,但需严格遵循规范以避免"面条代码"。以下是经过验证的适用场景及实践规范: --- #### 一、核心适用场景 1. **跳出深层嵌套循环** 当需要从多层循环(≥3层)中直接退出时,`goto`比多层`break`更简洁高效[^2][^3]: ```c for (int i=0; i<100; i++) { for (int j=0; j<100; j++) { for (int k=0; k<100; k++) { if (critical_error) goto EXIT_LOOPS; // 一次性跳出所有循环 } } } EXIT_LOOPS: printf("已退出循环"); // [^3] ``` 2. **集中式错误处理** 在资源分配场景(文件/内存)中,统一跳转到清理点: ```c FILE *fp = NULL; char *buf = NULL; fp = fopen("data.txt", "r"); if (!fp) goto CLEANUP; // 异常时跳转 buf = malloc(1024); if (!buf) goto CLEANUP; // 统一处理点 // ...正常逻辑... CLEANUP: // 资源统一释放 if (fp) fclose(fp); if (buf) free(buf); // [^5] ``` 3. **交互式流程控制** 需要重复输入直到满足条件时(替代`do-while`): ```c char input[20]; AGAIN: printf("请输入'确认'继续: "); scanf("%s", input); if (strcmp(input, "确认") != 0) goto AGAIN; // 直到输入正确[^4] ``` --- #### 二、使用规范与禁忌 | **允许场景** | **禁止场景** | 原因 | |--------------------------|-----------------------------|--------------------------| | 同函数内向后跳转 | 向前跳过变量初始化 | 未初始化风险[^5] | | 跳转距离<50行 | 跨函数跳转 | 语言禁止[^5] | | 标签全大写(如`CLEANUP`) | 循环替代(如`goto LOOP`) | 应使用`for/while`[^1] | | 错误处理/资源释放 | 制造非结构化的交叉跳转 | 可读性灾难[^2] | > ⚠️ **关键限制**:在C++中绝对禁止跳过构造函数(如`goto SKIP; MyClass obj;`),会导致未定义行为[^5]。 --- #### 三、替代方案推荐 1. **循环嵌套优化** ```c // 用函数替代多层循环 void process_layers() { for (int i=0; i<100; i++) { if (should_exit(i)) return; // 直接退出函数 } } ``` 2. **RAII模式(C语言模拟)** 通过作用域和清理函数模拟资源自动释放: ```c void cleanup(FILE **fp, char **buf) { if (*fp) fclose(*fp); if (*buf) free(*buf); } int main() { FILE *fp = fopen(...); char *buf = malloc(...); // 函数结束时显式调用cleanup() } // [^1] ``` --- #### 四、行业实践参考 - **Linux内核**:广泛用于错误处理(如驱动初始化失败时跳转到`err:`标签释放资源)[^5] - **嵌入式系统**:状态机实现中有限使用(如`STATE_A: ... goto STATE_B`) - **高性能计算**:极端优化场景(需配合详细注释) > 📌 **决策树**: > ```mermaid > graph TD > A[需跳出≥3层循环?] -->|是| B[使用goto] > A -->|否| C[用break/return] > D[需要集中资源释放?] -->|是| E[使用goto+清理标签] > D -->|否| F[使用函数分解] > ``` --- ### 总结 `goto`的合理使用需满足: 1. **场景限制**:仅用于错误处理/深层跳出/简单状态机 2. **距离限制**:跳转目标在当前屏幕可见范围内(<50行) 3. **安全限制**:绝不跳过初始化代码 4. **注释强制**:每个`goto`需说明必要性 > 经验法则:当代码中`goto`超过3次时,必须重构为函数或状态机[^2][^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值