我正在编写一个需要良好错误处理的C程序。代码喜欢这样:
If(doWork("A")<0){
return -1;
}
If(doWork("B")<0){
undoWork("A");
return -1;
}
If(doWork("C")<0){
undoWork("A");
undoWork("B");
return -1;
}
return 0;
此代码有效但看起来非常混乱,尤其是我有一长串doWork(X)
要调用。在这种情况下,是否有更好更清晰的方法来处理错误?
答案 0 :(得分:7)
有些人,尤其是初级到中级程序员,对于在生产代码中看到goto
有一种非常特殊的反应,但顺序获取资源的常用习惯用法及其出错时的智能释放如下: / p>
if(doWork("A") < 0)
goto errA;
if(doWork("B") < 0)
goto errB;
if(doWork("C") < 0)
goto errC;
/* success! */
return 0;
/* Error handling / releasing resources section */
errC:
undoWork("B");
errB:
undoWork("A");
errA:
return -1;
您会在系统代码中看到大量示例,例如:在linux内核中。
答案 1 :(得分:1)
作为相同的任务doWork
,您可以定义jobs
的链接列表或向量,并将其作为参数传递给doWork
,将相应的信息附加到此列表中功能,只需拨打undoWork
一次:
If(doWork("A", &jobs)<0){
return -1;
}
If(doWork("B", &jobs)<0){
undoWork(jobs);
return -1;
}
If(doWork("C", &jobs)<0){
undoWork(jobs);
return -1;
}
return 0;
这样,无论工作组合被撤消,你的逻辑都不会变得过于复杂。
与@ twain249的解决方案相比,优势在于该功能决定是否将作业添加到列表中,因此您可以获得良好的隔离,模块化。
您当然可以将某种形式的可互换数据结构与此相结合,以进一步减少重复代码的数量:
for(i=0; i < jobdata.size; i++) {
If(doWork(jobdata[i], &jobs)<0){
undowork(jobs);
return -1;
}
}
正如您所注意到的,数据结构设计在算法设计中起着重要作用,通常比通常认为的要重要得多。
可能有成千上万的工作,代码将保持四行。
答案 2 :(得分:0)
可能不是。像C ++和C#这样的新语言支持异常,以帮助改善这种情况。
也许你可以有一个表格,以某种方式表明你已经完成了哪些任务并撤消了这些任务。但我真的认为这会使你的代码更复杂而不是更少。
另请注意,尽管使用goto
有一些非常强烈的感觉,但事实上有时可以简化这样的结构。
答案 3 :(得分:0)
如果可以存储您必须在数组中调用doWork
的所有内容,那么您可以显着缩短代码。
int i = 0;
int len = MAX_NUM; //set to the value of calls
int error = 0;
for(i = 0; i < len; i++) {
if(doWork(a[i]) < 0) {
error = 1;
break;
}
}
if(error) {
for(int j = 0; j < i; i++) {
undoWork(a[j]);
}
return -1;
}
答案 4 :(得分:0)
如果您没有超长列表,可以这样处理。
if (dowork("A") >=0) {
if (dowork("B") >=0) {
if (dowork("C") >=0) {
if (dowork("D") >=0) return 0;
undowork("C"); }
undowork("B"); }
undowork("A"); }
return -1;
答案 5 :(得分:0)
还有一种基于单通道循环的广泛使用的方法,该方法是明确的并且不需要goto。这意味着撤销功能正确处理已完成的工作和未执行的工作。
do
{
if(doWork("A")<0)
break;
if(doWork("B")<0)
break;
if(doWork("C")<0)
break;
return 0;
}
while(0);
undoWork("A");
undoWork("B");
undoWork("C");
return -1;