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 。

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

时间: 2024-10-07 07:05:11

C语言之goto浅析的相关文章

java语言安全机制浅析

java通过所谓的沙箱安全模型保证了其安全性,下面我们就来看看java提供的安全沙箱机制. 组成沙箱的基本组件如下: 1.类装载器结构: 2.class文件检验器: 3.内置于java虚拟机(及语言)的安全特性: 4.安全管理器及java API. 一.类装载器体系结构 1.防止恶意代码去干涉善意的代码. 这是通过为不同类加载器提供不同的命名空间来实现的,在java虚拟机中,在同一个命名空间内的类可以直接进行交互,而不同的命名空间中类甚至不能觉察彼此的存在,除非显式地提供允许它们交互的机制. 2

易语言算法原理浅析【一】

注: 如果你看完了下面的文章.就来试试这个KeyGenMe吧,相信你能有所收获. http://www.52pojie.cn/thread-540179-1-1.html 一.文章开头首先我们要贴上一段易语言代码,并且编译这段代码,从汇编角度分析易语言程序编译后,易语言算法在汇编中的实现过程. .版本 2 .程序集 窗口程序集_启动窗口 .子程序 _按钮1_被单击 .局部变量 变量1, 整数型 .局部变量 变量2, 整数型 .局部变量 变量3, 整数型 变量2 = 1 变量3 = 2 变量1 =

C语言内存分配浅析

示例1: void myFun(int x); //声明也可写成:void myFun( int ); int main() { myFun(100);//一般的函数调用 return 0; } void myFun(int x) { printf("myFun: %d\n",x); } 我们一开始只是从功能上或者说从数学意义上理解myFun这个函数,知道myFun函数名代表的是一个功能(或是说一段代码).函数名到底又是什么东西呢? 函数指针变量 一个数据变量的内存地址可以存储在相应的

C/Go语言 Goto

goto语句_百度百科https://baike.baidu.com/item/goto%E8%AF%AD%E5%8F%A5/7603004 C语言中使用goto语句_鱼虾一整碗的博客-CSDN博客https://blog.csdn.net/baidu_36649389/article/details/54582619 C语言之goto浅析 - mr_yu - 博客园https://www.cnblogs.com/newbeeyu/p/5837347.html Go 语言 goto 语句 | 菜

go语言for的三种形式

在Go中其他循环遍历的控制语句,唯有for.而for同样也是比较灵活的: package main import "fmt" func main() {     // 最基本的一种,单一条件循环    // 这个可以代替其他语言的while循环    i := 1    for i <= 3 {        fmt.Println(i)        i = i + 1    }     // 经典的循环条件初始化/条件判断/循环后条件变化    for j := 7; j &

c语言捕捉异常

闲暇之日阅读lua源码,发现原来C语言除goto之外的另一个处理异常的方法.既为setjump longjump两个函数,setjump相当于try,longjump相当于catch.与goto不同的是,longjump是全局的,比goto的作用范围更广.下面贴出简单用法: #include <stdlib.h> #include <setjmp.h> jmp_buf jumper; int fdf(int a, int b) { if (b == 0) { // can't di

Go语言循环判断的使用~

Go 语言条件语句 条件语句需要开发者通过指定一个或多个条件,并通过测试条件是否为 true 来决定是否执行指定语句,并在条件为 false 的情况在执行另外的语句. 下图展示了程序语言中条件语句的结构: if 语句由布尔表达式后紧跟一个或多个语句组成. 语法 Go 编程语言中 if 语句的语法如下: if 布尔表达式 { // 在布尔表达式为 true 时执行 } 栗子~ 1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 func main()

Go语言开发(二)、Go语言基础

Go语言开发(二).Go语言基础 一.Go语言程序结构 Go语言程序基本结构如下:A.包声明B.引入包C.函数D.变量E.语句 & 表达式F.注释 package main //包声明 import "fmt" //引入包 func main(){ //main函数 //打印Hello World fmt.Print("Hello World!") } 二.Go语言基础语法 1.Go语言标记 Go语言程序由多个标记组成,可以是关键字.标识符.常量.字符串.符

Go语言流程控制

目录 顺序控制 分支控制 switch 循环控制 for break continue goto 在程序中,程序运行的流程控制决定程序是如何执行的,是我们必须掌握的,主要有三大流程控制语句. 顺序控制 分支控制 循环控制 顺序控制 程序从上到下逐行的执行,中间没有任何判断和跳转. 分支控制 让程序有选择的执行,分支控制有三种: (1)单分支 if 条件表达式 { //执行代码块 } (2)双分支 if 条件表达式 { //执行代码块 } else { //执行代码块 } (3)多分支 if 条件