工作中经常会看到这样的接口代码,先获取一个资源(比如文件描述符、动态申请的内存等),进行相应处理,如果处理正常,会申请另外资源再进行相应处理,前面获取的资源也会在下面继续使用,如果中间处理存在异常,就需要进行各种资源释放操作,类似下面这样的代码处理流程
int main(int argc, char *argv[])
{
int fd = -1;
char *buf = NULL;
off_t file_len = 0;
int ret = -1;
fd = open("./test.txt", O_RDWR);
if (-1 == fd)
{
return -1;
}
file_len = lseek(fd, 0, SEEK_END);
if (-1 == file_len)
{
close(fd);
return -1;
}
buf = malloc(file_len);
if (NULL == buf)
{
close(fd);
return -1;
}
// 需要将文件偏移指针重新指向文件头
ret = lseek(fd, 0, SEEK_SET);
if (ret != 0)
{
close(fd);
free(buf);
return -1;
}
ret = read(fd, buf, file_len);
if (-1 == ret)
{
close(fd);
free(buf);
return -1;
}
printf("buf:%s\n", buf);
close(fd);
free(buf);
return 0;
}
可以看到,就这样的一个小功能,中间就涉及很多处资源的释放操作。而实际开发中接触的接口往往涉及更多需要释放的资源,中间一处出错,就需要多条语句进行释放操作,而且是很多出错的地方都要这样执行。
这种场景如果使用goto语句就会好很多,比如修改后:
int main(int argc, char *argv[])
{
int fd = -1;
char *buf = NULL;
off_t file_len = 0;
int ret = -1;
fd = open("./test.txt", O_RDWR);
if (-1 == fd)
{
return -1;
}
file_len = lseek(fd, 0, SEEK_END);
if (-1 == file_len)
{
goto CLOSE_FD;
ret = -1;
}
buf = malloc(file_len);
if (NULL == buf)
{
goto CLOSE_FD;
ret = -1;
}
// 需要将文件偏移指针重新指向文件头
ret = lseek(fd, 0, SEEK_SET);
if (ret != 0)
{
goto FREE_BUF;
ret = -1;
}
ret = read(fd, buf, file_len);
if (-1 == ret)
{
goto FREE_BUF;
ret = -1;
}
ret = 0;
printf("buf:%s\n", buf);
FREE_BUF:
free(buf);
CLOSE_FD:
close(fd);
return ret;
}
上面的修改可以看出几点:
1、所有的资源释放操作都放在函数最后,越往后申请的资源则是放在前面进行释放,这样就不怕中间错误处理跳转前面申请的资源没有释放。
2、涉及新资源的释放,会有一个新的goto标号,且最好跟所要涉及的释放资源相吻合。
3、goto标号最好顶头写,不要存在缩进,这样易于识别查看
goto语句除了上面进行错误处理释放资源这样的操作,其他情况估计大家也都看多了书上说的,还是要慎用。