zoukankan      html  css  js  c++  java
  • C语言之goto浅析

    1.  读代码时遇了的疑惑点:

    static int
    do_bind(const char *host, int port, int protocol, int *family) {
        int fd;
        int status;
        int reuse = 1;
        struct addrinfo ai_hints;
        struct addrinfo *ai_list = NULL;
        char portstr[16];
        if (host == NULL || host[0] == 0) {
            host = "0.0.0.0";    // INADDR_ANY
        }
        sprintf(portstr, "%d", port);
        memset( &ai_hints, 0, sizeof( ai_hints ) );
        ai_hints.ai_family = AF_UNSPEC;
        if (protocol == IPPROTO_TCP) {
            ai_hints.ai_socktype = SOCK_STREAM;
        } else {
            assert(protocol == IPPROTO_UDP);
            ai_hints.ai_socktype = SOCK_DGRAM;
        }
        ai_hints.ai_protocol = protocol;
    
        status = getaddrinfo( host, portstr, &ai_hints, &ai_list );
        if ( status != 0 ) {
            return -1;
        }
        *family = ai_list->ai_family;
        fd = socket(*family, ai_list->ai_socktype, 0);
        if (fd < 0) {
            goto _failed_fd;
        }
        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(int))==-1) {
            goto _failed;
        }
        status = bind(fd, (struct sockaddr *)ai_list->ai_addr, ai_list->ai_addrlen);
        if (status != 0)
            goto _failed;
    
        freeaddrinfo( ai_list );
        return fd;
    _failed:
        close(fd);
    _failed_fd:
        freeaddrinfo( ai_list );
        return -1;
    }
    
    static int
    do_listen(const char * host, int port, int backlog) {
        int family = 0;
        int listen_fd = do_bind(host, port, IPPROTO_TCP, &family);
        if (listen_fd < 0) {
            return -1;
        }
        if (listen(listen_fd, backlog) == -1) {
            close(listen_fd);
            return -1;
        }
        return listen_fd;
    }

    这是一段创建协议无关的监听套接字的代码,其中有三处用到了 goto 语句,由于前边调用了  getaddrinfo(...)函数,该函数会自动申请内核的空间,所以需要在结束后调用 freeaddrindo(...)来释放空间.

    但是当读到 goto _failed 时,产生疑惑因为 _failed:标号只有一个 close(fd);

    由于之前没用过这个知识点,以为运行完close(fd)后会直接退出函数,因此疑惑为什么没有调用 getaddrinfo(...),还以为是作者的失误(...),但一想觉得又不可能是作者的问题,结果 查看 K&R<<C程序设计语言>>的相关内容,发现是自己的问题。。。

    原来goto一般是 跑到goto语句所指向的标号处,使得程序从该标号处开始向下执行,一般起到跳过调用goto开始到标号的中间要执行的代码的作用。 类似上例中,当调用完_failed: close(fd)以后,函数不会退出,而是会继续执行,接着调用_failed_fd:处的语句,直到return -1程序结束。

    这样一来便调用到了freeaddrinfo(...). 疑惑迎刃而解。

    2.goto分析:

      1).goto是干啥的?

      c语言提供了可随意滥用的goto语句以及标记跳转位置的标号.但是理论上goto是没有必要的.

      标记跳转位置的标号: 如上例中的  _failed: , _failed_fd:, 标号可以在该函数的任意地方,在标号后边,写处理逻辑。

      调用goto语句语法: “goto 标号;" 

      2)测试代码:

      

    #include <stdio.h>
    #include <stdlib.h>
    
    int
    main(int argc, char *argv[])
    {
        int a = atoi(argv[1]);
    
        if (a == 1)
        {
            goto failed1;
        }
        else if(a == 2)
        {
            goto failed2;
        }
        else
        {
            goto failed3;
        }
        
    failed1:
        printf("get failed1
    ");
    failed2:
        printf("get failed2
    ");
    failed3:
        printf("get failed3
    ");
    
        printf("a + b = 3
    "); (随便输出的)
        return 0;
    }

    代码从控制台输入a的值,下面分别是 a = 1, 2, 3时的结果,编译:gcc -g -o test test.c

    控制台输入: ./test 1

    结果:

    get failed1

    get failed2

    get failed3

    a + b = 3

    控制台输入: ./test 2

    结果:

    get failed2

    get failed3

    a + b = 3

    控制台输入: ./test 3

    结果:

    get failed3

    a + b = 3

    可以看到,运行到对应的标号后,程序是继续向下运行的。

    修改代码:

    将 _failed1:的代码搬到 if(a == 1) 上边,同时修改a的值:

    #include <stdio.h>
    #include <stdlib.h>
    
    int
    main(int argc, char *argv[])
    {
        int a = atoi(argv[1]);
    
    failed1:
        printf("get failed1
    ");
        a = 3;
        
        if (a == 1)
        {
         printf("a = 1 ");
    goto failed1; } else if(a == 2) { goto failed2; } else { goto failed3; } failed2: printf("get failed2 "); failed3: printf("get failed3 "); printf("a + b = 3 "); return 0; }

    当再次编译代码,并运行:

    ./test 1

    输出结果:

    get failed1

    get failed3

    a + b = 3

    可以看出,标号仅仅起到一个标识作用,程序顺序执行时,标号仿佛不存在,首先输出get failed1, 然后修改 a = 3,.并不是只有调用了goto标号,然后程序调到标号处,从而触发标号下边的代码开始运行。而是可以看作在完整的代码中插入标号,从而单纯地引导goto到达的位置。

      3)为什么不推荐使用goto:

      一般认为goto的使用会造成代码难以理解和维护,ps:因为我用的较少,对此理解还不是很深刻。

      4)什么情况下用到goto:

      当程序有多层嵌套,当处在嵌套内的逻辑判断为真或为假时,需要彻底或者连续跳出几层循环时,一般考虑使用goto,因为break一次只能跳出一层,并且需要跳出多层循环时需要假如更多的判断逻辑,

      这种情况下,会考虑使用goto,还有就是在大型程序中处理复杂逻辑时,一般也会考虑使用goto。

      例如判断两个数组中是否有相同元素时:

      

    //use goto
    for(i = 0, i < n; ++i)
    {
        for (j = 0, j < m; ++j)
        {
            if(a[i] == b[j])
            {
                goto found;
            }
        }
    }
    
    found:
        ...
        ...
    
    //do not use goto
    for(i = 0, i < n; ++i)
    {
        for (j = 0, j < m; ++j)
        {
            if(a[i] == b[j])
            {
                found = true;
                break;
            }
        }
        if (found)
        {
            break;
        }
    }
    
    ...
    ...

     总之是把 double-edged sword 。

    先总结到这里,文章中难免这样那样的错误,欢迎指正。

  • 相关阅读:
    每天一道leetcode 搜索旋转排序数组(二分法)
    每天一道leetcode 统计重复个数(循环节)
    python3 简单web目录扫描脚本(后续更新完整)
    每天一道leetcode 盛最多水的容器 (双指针)
    python3 语法学习 类和继承
    python3 语法学习 文件操作及os方法
    python3 语法学习 输入输出美观
    TCP/IP 协议:IP 协议
    TCP/IP 协议:链路层概述
    Http权威指南(二)---读书笔记
  • 原文地址:https://www.cnblogs.com/newbeeyu/p/5837347.html
Copyright © 2011-2022 走看看