2012-04-07 31 views
7

Tôi đang viết chương trình C cần xử lý lỗi tốt. Mã này thích như sau:Phương pháp xử lý lỗi sạch trong 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; 

Mã này hoạt động nhưng trông rất lộn xộn, đặc biệt là tôi có danh sách dài doWork(X) để gọi. Có một cách tiếp cận tốt hơn và sạch hơn để xử lý lỗi trong trường hợp này?

+1

có thể trùng lặp của [Cách tránh chuỗi dài miễn phí (hoặc xóa) sau mỗi lần kiểm tra lỗi trong C?] (Http://stackoverflow.com/questions/3339946/how-to-avoid-long- chain-of-frees-hoặc-deletes-after-every-error-check-in-c) – karlphillip

+1

Có nhiều câu trả lời hay, nhưng điều tốt nhất tôi có thể đưa ra là bình luận để đi với họ: bất kỳ cách tiếp cận nào bạn thực hiện, bạn ' sẽ luôn luôn giảm thiểu sự xấu xí như thế này, và tối đa hóa độ mạnh mẽ, bằng cách giảm thiểu số lượng các bước có trường hợp lỗi. Ví dụ, precomputing số lượng bộ nhớ bạn sẽ cần và thực hiện một 'malloc' duy nhất trước khi bạn bắt đầu thay vì phân bổ số lượng nhỏ ở mỗi bước có thể làm cho nhiều nhiệm vụ phức tạp với logic lỗi đơn giản. –

Trả lời

7

Một số người, đặc biệt là các lập trình viên mới bắt đầu-to-trung gian, có một phản ứng rất mang phong cách riêng để nhìn thấy goto trong mã sản xuất, nhưng các thành ngữ thông thường cho tuần tự mua lại các nguồn lực và phát hành thông minh của họ khi lỗi như sau:

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; 

Bạn sẽ thấy nhiều ví dụ trong mã hệ thống, ví dụ: trong hạt nhân Linux.

0

Có thể là không. Các ngôn ngữ mới hơn như C++ và C# ưu tiên ngoại lệ để giúp cải thiện tình huống giống như thế này.

Có lẽ bạn có thể có một bảng bằng cách nào đó cho biết bạn đã thực hiện tác vụ nào và hoàn tác những tác vụ đó. Nhưng tôi thực sự nghĩ rằng sẽ làm cho mã của bạn phức tạp hơn và không ít hơn. Cũng cần lưu ý rằng, mặc dù có một số cảm giác khá mạnh về việc sử dụng goto, nhưng trong thực tế, khi đó có thể đơn giản hóa các cấu trúc như thế này.

+0

Ngay cả các ngôn ngữ mới hơn như golang thậm chí còn có cấu trúc tốt hơn như 'trì hoãn'. – Flavius

0

nếu có thể lưu trữ tất cả mọi thứ bạn phải gọi doWork trên trong một mảng thì bạn có thể rút ngắn mã đáng kể giống như vậy.

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; 
} 
1

Là nhiệm vụ tương tự doWork, có lẽ bạn có thể định nghĩa một danh sách liên kết hoặc vector của jobs và thông qua đó như một tham số để doWork, thêm các thông tin tương ứng với danh sách này bên trong hàm, và chỉ gọi undoWork một lần:

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; 

Bằng cách này, logic của bạn sẽ không trở nên quá phức tạp, bất kể kết hợp công việc sẽ được hoàn tác.

Lợi thế, so với giải pháp @ twain249, là chức năng quyết định xem một công việc có được thêm vào danh sách hay không, vì vậy bạn đã có sự cô lập, mô đun tốt đẹp.

Bạn có thể dĩ nhiên kết hợp một số hình thức của một cấu trúc dữ liệu interable với điều này, để tiếp tục giảm số lượng mã lặp đi lặp lại:

for(i=0; i < jobdata.size; i++) { 
    If(doWork(jobdata[i], &jobs)<0){ 
     undowork(jobs); 
     return -1; 
    } 
} 

Như bạn có thể nhận thấy, thiết kế cấu trúc dữ liệu đóng một vai trò quan trọng trong việc thiết kế thuật toán, thường là một trong những quan trọng hơn nhiều so với một trong những thường nghĩ.

Có thể có hàng nghìn công việc, mã sẽ vẫn là bốn lớp lót.

0

Nếu bạn không có danh sách dài, bạn có thể tiếp cận danh sách theo cách này.

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; 
+0

Tôi không hiểu tại sao mọi người lại bỏ phiếu này. Khi biên dịch nó sẽ được chính xác giống như làm kỹ thuật goto/nhãn. – EdH

0

Ngoài ra còn có một cách tiếp cận được sử dụng rộng rãi khác dựa trên một vòng lặp vượt qua rõ ràng và không yêu cầu goto.Nó ngụ ý rằng các chức năng Hoàn tác xử lý chính xác cả công việc đã được thực hiện và điều đó không đúng.

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; 
Các vấn đề liên quan