嵌入式技术
为了写出适应性更广的C语言程序,程序员考虑问题时应面面俱到。例如,在C语言程序中调用 open() 函数尝试打开文件时,应考虑到文件是否存在,当前程序是否有足够权限等情况。在打开文件失败时,需要做相应的错误处理,这样才能让程序的稳定性更强。
看到这里,相信有读者已经察觉到了,错误处理语句会让整个代码繁琐许多。例如:int fd = open("filename", O_RDWR);if(fd < 0){printf("open file failed, %m ");return -1;}if(sizeof("hello") != write(fd, "hello", sizeof("hello")) ){printf("write file failed, %m "); close(fd);return -1;}close(fd);
上面这段C语言代码做了相应的错误处理,能够处理打开文件失败,和写数据失败的情况。但是多数情况下,我们的C语言程序都不太可能去操作一个不允许操作,或者不存在操作的文件,而写数据也很少出现失败的情况。事实上,如果不考虑极少出现的意外,上述C语言代码可以这么写:int fd = open("filename", O_RDWR);write(fd, "hello", sizeof("hello"));close(fd);
显然,代码简洁多了。可是,虽然“意外”出现的几率比较低,我们仍然不能忽略它,否则一旦“意外”出现,程序崩溃退出还好,万一程序继续运行,造成不可预知的错误就麻烦了。所以,在编写C语言程序时,添加相应的错误判断和处理语句是必要的。
在C语言程序开发中,某个函数的执行状态常常使用返回码区分,也即 return 的值。究竟函数应该用什么样的返回值,决定什么执行状态,并没有强制标准。不过,对于 int 返回值类型的函数,大多数C语言程序员都爱使用 return 0; 表示函数执行成功,函数执行失败时,则返回一个负值(如 return -1;)。
请看下面这个函数:int test(int val){if(val % 99999 == 0)return -1;printf("val = %d ", val);return0;}从上述C语言代码可以看出,test() 函数接收一个 int 型的参数 val,如果 val 为 99999 的倍数,则返回 -1(这里认为出错),否则打印出 val 的值并返回 0。
按照上面的讨论,开发C语言程序时应考虑各种“意外”,在调用 test() 函数时,需要相应的错误处理代码:if(-1 == test(val)){// do somethingreturn -1;}
在实际的C语言项目开发中,有时会遇到重复调用某个函数的情况,例如:if(-1 == test(a)){// do somethingreturn -1;}if(-1 == test(b)){// do somethingreturn -1;}if(-1 == test(c)){// do somethingreturn -1;}...if(-1 == test(m)){// do somethingreturn -1;}
test() 函数出错的几率很小,但是为了程序的稳定性,仍然需要相应的错误处理代码。但是错误处理也让本来很简洁的代码段变得“啰嗦”,而且这些错误处理代码被执行的可能性微乎其微。这里的test() 函数只是为了讨论主题提出的例子。读者应考虑实际情况,例如:没有程序员会调用 open() 打开一个不允许操作的文件,但是错误处理代码仍然是不可少的。
当然,重复的代码可以使用宏定义封装,这一点之前的文章已经讨论过,不再赘述了。值得说明的另外一个办法就是利用 test() 执行成功时返回值为 0 的特点,请看下面这段C语言代码:int ret = 0;ret += test(a);ret += test(b);ret += test(c);...ret += test(m);if(0!=ret){// do somethingreturn -1;}
可以看出,这样就只需写一处错误处理代码了,而且只要有一个 test() 执行失败,C语言程序就会执行它。这样是一个折中,在尽力维持代码简洁性的基础上,保留错误处理逻辑。显然,只有 test() 的错误无需立刻处理时才能这么写。
一般来说,使用 ret+=test 折中方案只适合无需立刻处理错误的情况,此时所有的 test() 调用都被视为等价的操作。所以在错误处理代码中,常常不再考虑究竟是哪一个 test() 出错。不过,如果希望知道究竟是哪个或者哪几个函数 test() 出错,也是有办法的。
显然,应该从 test() 函数本身入手。最简单的办法就是在 test() 函数返回 -1 之前打印出输入的参数,修改后的 test() 的C语言代码如下:int test(int val){if(val % 99999 == 0){printf("unexpected val: %d ", val);return -1;}printf("val = %d ", val);return0;}
这样就可以通过输入的参数 val 确定哪一个 test() 出错了。不过这种方法要求各个 test() 接收到的参数各不相同,否则就失效了。
幸好还有其他调试手段。如果能够知道 test() 函数的调用链(我之前的文章讨论过“调用链”这个概念),那C语言程序的出错路径也就显而易见了。所以,要定位究竟哪一个 test() 出错,只需在 test() 出错时将函数调用链打印出来就可以了。
全部0条评论
快来发表一下你的评论吧 !