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

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

代码从控制台输入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[]); failed1:
printf("get failed1\n");
a = 3; if (a == )
{
     printf("a = 1\n");
goto failed1;
}
else if(a == )
{
goto failed2;
}
else
{
goto failed3;
} failed2:
printf("get failed2\n");
failed3:
printf("get failed3\n"); printf("a + b = 3\n");
return ;
}

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

./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 = , i < n; ++i)
{
for (j = , j < m; ++j)
{
if(a[i] == b[j])
{
goto found;
}
}
} found:
...
... //do not use goto
for(i = , i < n; ++i)
{
for (j = , j < m; ++j)
{
if(a[i] == b[j])
{
found = true;
break;
}
}
if (found)
{
break;
}
} ...
...

 总之是把 double-edged sword 。

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

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

  1. C语言进阶——goto 和 void 的分析08

    遭人遗弃的goto: 高手潜规则:禁止使用goto 项目经验:程序质量与goto的出现次数成反比 最后的判决:将goto打入冷宫 程序示例1:(goto副作用分析) #include <stdi ...

  2. go语言之goto语句和函数和defer语句

    1.goto关键字 import "fmt" func main() { for i := 0;i <11;i++{ if i == 2{ //关键字,goto跳转到某个位置 ...

  3. C语言序列点浅析

    摘要: 现行国内的C语言教材普遍不介绍序列点,这使得读者只能“死记硬背”有序列点表达式的求值顺序,不仅造成了读者对C语言知识的认知残缺不全,而且也影响了读者学习的积极性.本文总结了序列点的作用,即表达 ...

  4. Go 语言 goto 语句

    Go 语言的 goto 语句可以无条件地转移到过程中指定的行. goto语句通常与条件语句配合使用.可用来实现条件转移, 构成循环,跳出循环体等功能. 但是,在结构化程序设计中一般不主张使用goto语 ...

  5. JAVA语言中冒号的用法

    近来由于本人要介入android平台的开发,所以就买了本JAVA语言的书学习.学习一段时间来,我的感觉是谭浩强就是厉害,编写的<C编程语言>系列丛书不愧是经典.书中对C语言的介绍既系统又全 ...

  6. Go语言循环判断的使用~

    Go 语言条件语句 条件语句需要开发者通过指定一个或多个条件,并通过测试条件是否为 true 来决定是否执行指定语句,并在条件为 false 的情况在执行另外的语句. 下图展示了程序语言中条件语句的结 ...

  7. c语言捕捉异常

    闲暇之日阅读lua源码,发现原来C语言除goto之外的另一个处理异常的方法.既为setjump longjump两个函数,setjump相当于try,longjump相当于catch.与goto不同的 ...

  8. Go语言流程控制

    1.条件语句 几个注意点和C#不一样的. { } else { } ① 条件语句不需要使用括号将条件包含起来 a<5 ,C#必须有() ②无论语句体内有几条语句,花括号{}都是必须存在的:C#如 ...

  9. Go语言学习笔记六: 循环语句

    Go语言学习笔记六: 循环语句 今天学了一个格式化代码的命令:gofmt -w chapter6.go for循环 for循环有3种形式: for init; condition; increment ...

随机推荐

  1. PHP程序员的技术成长规划 第一阶段:基础阶段

    第一阶段:基础阶段(基础PHP程序员) 重点:把LNMP搞熟练(核心是安装配置基本操作)目标:能够完成基本的LNMP系统安装,简单配置维护:能够用PHP源码做基本的简单系统的PHP开发:能够在PHP中 ...

  2. Delphi主线程重入而导致程序卡死的解决方案

    Delphi的线程可以通过调用AThread.Synchronize(AProc),可以将Proc放入主线程中同步运行,此时AThread将挂起,直到主线程执行完AProc. 如果有BThread,调 ...

  3. A1037

    给两个序列,一一对应相乘,求最大和. 0不算数,输入时按正负共分为4个数组. #include<cstdio> #include<algorithm> #include< ...

  4. c语言异常处理机制

    异常处理机制:setjmp()函数与longjmp()函数 C标准库提供两个特殊的函数:setjmp() 及 longjmp(),这两个函数是结构化异常的基础,正是利用这两个函数的特性来实现异常. 所 ...

  5. Python学习之——Python安装

    环境:Centos6.5+python2.7.5 1.centons6.5系统中是已经安装了python的,先查看版本是不是需要的 python --version 2.安装一些必要的包,防止后面需要 ...

  6. ubuntu18.04 VirtualBox 开启虚拟机出错 Kernel driver not installed (rc=-1908)

    写的很明白了 提示缺少GCC PERL MAKE,安装 重试..... 重启VM 搞定....

  7. 记数据结构MOOC-二叉树

    主要的学习内容 在本章中,主要学习了二叉树的实现以及各种遍历的方法.着重介绍了前序.中序.后序三种遍历方法的递归实现,同时也描述了前序中序遍历的迭代方法. 教材的主要内容 教材是以哈夫曼编码树为主要脉 ...

  8. 20155304 2016-2017-2《Java程序设计》课程总结

    20155304 2016-2017-2<Java程序设计>课程总结 (按顺序)每周作业链接汇总 预备作业1:对自己专业看法及.学习Java的期望,以及心中的师生关系 预备作业2:有关技能 ...

  9. 20155307 实验一《Java开发环境的熟悉》实验报告

    (一)命令行下Java程序开发 题目:实现Fibonacci数列功能,并进行测试 命令行下的程序截图 上传到了码云: 遇到的问题? 可以直接使用Javac,不加环境变量也行,但是文件的名字与类名必须一 ...

  10. Hadoop守护进程的作用(转)

    概述: <ignore_js_op> Hadoop是一个能够对大量数据进行分布式处理的软件框架,实现了Google的MapReduce编程模型和框架,能够把应用程序分割成许多的 小的工作单 ...