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\n");
failed2:
    printf("get failed2\n");
failed3:
    printf("get failed3\n");

    printf("a + b = 3\n"); (隨便輸出的)
    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\n");
    a = 3;
    
    if (a == 1)
    {
     printf("a = 1\n");
goto failed1; } else if(a == 2) { goto failed2; } else { goto failed3; } failed2: printf("get failed2\n"); failed3: printf("get failed3\n"); printf("a + b = 3\n"); 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 。

 

先總結到這里,文章中難免這樣那樣的錯誤,歡迎指正。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM