Câu hỏi liên quan

0 Phiếu
1 Trả lời
0 Phiếu
0 Câu trả lời
0 Phiếu
3 Câu trả lời
0 Phiếu
5 Câu trả lời
0 Phiếu
2 Câu trả lời

Cách tao nhã cho thoát khỏi function một gọn gàng mà không sử dụng goto trong C


0 Phiếu
Đã hỏi 25/5/2016 bởi Pvg_I (1,160 điểm)
Chúng tôi thường xuyên viết một số chức năng mà có nhiều hơn một lối ra điểm (tức là return c). Cùng lúc đó, khi thoát khỏi chức năng, cho một số công trình nói chung như dọn dẹp resource , chúng tôi muốn thực hiện chúng chỉ một lần, thay vì thực hiện chúng tại mỗi điểm xuất cảnh. Thông thường, chúng ta có thể đạt được mong muốn của chúng tôi bằng cách sử dụng goto như sau:
void f()
{
    ...
    ...{..{... if(exit_cond) goto f_exit; }..}..
    ...
    f_exit:
    some general work such as cleanup
}
tôi nghĩ rằng bằng cách sử dụng goto ở đây là được chấp nhận, và tôi biết nhiều người đồng ý sử dụng goto ở đây. chỉ cần ra khỏi tò mò , có không tồn tại bất kỳ cách thanh lịch cho gọn gàng có thể thoát khỏi một function mà không cần sử dụng goto trong C?

5 Câu trả lời

0 Phiếu
Đã trả lời 04/6/2016 bởi Fbz7912 (530 điểm)
được bình chọn là câu trả lời hay nhất 06/6/2016 bởi Times
 
Câu trả lời hay nhất
Tôi đã nhìn thấy rất nhiều các giải pháp làm thế nào để làm điều này và họ có xu hướng che khuất, không đọc được và xấu xí tại một số mức độ. Cá nhân tôi nghĩ cách ít xấu là điều này:
int func (void)
{
  if(some_error)
  {
    cleanup();
    return result;
  }
  ...
  if(some_other_error)
  {
    cleanup();
    return result;
  }
  ...
  cleanup();
  return result;
}
Vâng, nó sử dụng hai hàng code thay vì một. Như vậy? Nó là rõ ràng, dễ đọc, duy trì. Đây là một ví dụ về nơi bạn có để chiến đấu đầu gối-jerk phản xạ của bạn chống lại sự lặp lại code và sử dụng thông thường. function dọn dẹp được viết chỉ là một lần, tất cả làm sạch lên code là tập trung đó.
Đã bình luận 05/6/2016 bởi zbe1889Remis (160 điểm)
@ BasileStarynkevitch phát biểu cá nhân, tôi nghĩ rằng các giải pháp goto là chấp nhận được, nhưng không phải là thanh lịch. Bây giờ tôi sử dụng giải pháp goto trong chương trình của tôi đâu có tồn tại điểm xuất cảnh trong nested vòng/trường hợp do đó không 'do {.} while(0)' hay 'switch() {}' giải pháp giúp, nhưng tôi thực sự muốn biết liệu có những giải pháp tốt hơn.
Đã bình luận 06/6/2016 bởi Think_9049 (440 điểm)
Đó cũng là [GOTO vẫn được coi là độc hại] (http://stackoverflow.com/questions/ 46586/goto-vẫn-coi-hại /) mà bao gồm nhiều mặt đất cùng một.
Đã bình luận 06/6/2016 bởi Jbe3114 (150 điểm)
Mặc dù các cuộc thảo luận chung về GOTO cover tương tự như mặt đất, câu hỏi này là đặc biệt về thực hiện một số code phổ biến trước khi function trả về cho người gọi của mình. Không có các giải pháp hợp lý để làm như vậy mà không liên quan đến GOTO.
+2 Phiếu
Đã trả lời 03/6/2016 bởi smalt_Phys (700 điểm)
Ví dụ:
void f()
{
    do
    {
         ...
         ...{..{... if(exit_cond) break; }..}..
         ...
    }  while ( 0 );
    some general work such as cleanup
}
hoặc bạn có thể sử dụng
while ( 1 )
{
   //...
}
cấu trúc sau các lợi thế chính của các phương pháp cấu trúc ngược với sử dụng goto tuyên bố là nó giới thiệu một kỷ luật bằng văn bản mã. Tôi chắc chắn và có đủ kinh nghiệm rằng nếu một function có một goto statement sau đó thông qua một số thời gian nó sẽ có một số goto cáo.:)
Đã bình luận 03/6/2016 bởi Thesleet (130 điểm)
Tại sao ông lại hỏi? Tò mò về kỳ quái control luồng hoạt động? Và tiêu chí của bạn cho sự sang trọng là gì?
Đã bình luận 05/6/2016 bởi Canloree (270 điểm)
Thêm một mức độ phụ {} và thụt lề, hoặc thậm chí một function (hoặc vĩ mô!) là khó khăn hơn để đọc và hơn accident-prone hơn một bước rõ ràng để một điểm xuất cảnh được đặt tên theo. IMnsvHO
0 Phiếu
Đã trả lời 04/6/2016 bởi buiebao (180 điểm)
tôi đoán rằng thanh lịch có thể có nghĩa là cho bạn lạ và rằng bạn chỉ đơn giản là muốn tránh từ khóa goto, vì vậy... bạn có thể xem xét sử dụng setjmp(3) longjmp:
void foo() {
jmp_buf jb;
if (setjmp(jb) == 0) {
   some_stuff();
   //// etc...
   if (bad_thing() {
       longjmp(jb, 1);
   }
 };
};
tôi không có ý tưởng nếu nó phù hợp với tiêu chí sang trọng của bạn. (Tôi tin rằng nó không phải là rất thanh lịch, nhưng đây là chỉ là một ý kiến ; Tuy nhiên, đó là không có goto rõ ràng). Tuy nhiên, những điều thú vị là longjmp đó là một -local nhảy : bạn có thể có thông qua (gián tiếp) jb để some_stuff và có một số routine (ví dụ như được gọi là some_stuff) làm longjmp. Điều này có thể trở thành code không thể đọc được (vì vậy nhận xét nó một cách khôn ngoan). Thậm chí uglier hơn longjmp: sử dụng (trên Linux) setcontext(3) đọc continuations ngoại lệ (và các cuộc gọi/cc operation trong chương trình). Và tất nhiên, tiêu chuẩn exit(3) là một cách trang nhã (và hữu ích) để đi ra khỏi một số chức năng. Bạn đôi khi có thể chơi gọn gàng lừa bằng cách sử dụng cũng atexit(3) BTW, Linux kernel code sử dụng khá thường xuyên goto bao gồm trong một số code được coi là thanh lịch. Điểm của tôi là: IMHO không là cuồng tín đối với goto -s kể từ khi có những trường hợp sử dụng (với việc chăm sóc), nó là trong thực tế trang nhã.
Đã bình luận 05/6/2016 bởi Voran (330 điểm)
@ BasileStarynkevitch rõ ràng là tôi biết sang trọng là một vấn đề ý kiến, nhưng hầu hết chúng ta có một số phán quyết cơ bản ở thanh lịch chung và chúng tôi có nhiều thỏa thuận chung trong việc thiết kế chương trình như vậy gọi là '' tao nhã".
0 Phiếu
Đã trả lời 04/6/2016 bởi V7995 (870 điểm)

tại sao tránh goto?

vấn đề bạn muốn giải quyết là: làm thế nào để đảm bảo rằng một số phổ biến code luôn luôn được thực hiện trước khi function trả về cho người gọi không? đây là một vấn đề cho các lập trình C, vì C không cung cấp bất kỳ hỗ trợ xây dựng trong RAII. Như bạn đã thừa nhận trong cơ thể câu hỏi của bạn, goto là một giải pháp hoàn toàn chấp nhận được . Không bao giờ-the-ít, có thể có các lý do kỹ thuật để tránh sử dụng nó:
  • học tập
  • mã hóa tiêu chuẩn tuân thủ
  • cá nhân whim (mà tôi nghĩ rằng những gì động cơ thúc đẩy câu hỏi này)
có luôn nhiều hơn là một cách để da con mèo, nhưng sang trọng như là một tiêu chí là quá chủ quan để cung cấp một cách để thu hẹp để thay thế tốt nhất duy nhất. Bạn phải quyết định lựa chọn tốt nhất cho chính mình.

một cách rõ ràng gọi một dọn dẹp function

nếu tránh một code dọn dẹp phổ biến rõ ràng nhảy (ví dụ: goto hoặc break) có thể được đóng gói trong một chức năng, và rõ ràng gọi là ở điểm đầu return.
int foo () {
    ...
    if (SOME_ERROR) {
        return foo_cleanup(SOME_ERROR_CODE, ...);
    }
    ...
}
(điều này tương tự như một câu trả lời đăng, mà tôi chỉ thấy sau khi tôi ban đầu đăng, nhưng form Hiển thị ở đây có thể tận dụng lợi thế của anh chị em gọi tối ưu hóa.) một số người cảm thấy explicitness là rõ ràng hơn, và do đó hơn tao nhã. Những người khác cảm thấy sự cần thiết để vượt qua đối số dọn dẹp cho function là một detractor lớn.

thêm một lớp về mình.

mà không thay đổi ngữ nghĩa của API người sử dụng, thay đổi thực hiện của nó vào một wrapper bao gồm hai phần. Một phần thực hiện các công việc thực tế của các chức năng. Phần hai hoạt động dọn dẹp cần thiết sau phần một được thực hiện. Nếu mỗi phần được đóng gói trong chức năng riêng của mình, wrapper function có một thực hiện rất sạch sẽ.
struct bar_stuff {...};
static int bar_work (struct bar_stuff *stuff) {
    ...
    if (SOME_ERROR) return SOME_ERROR_CODE;
    ...
}
int bar () {
    struct bar_stuff stuff = {};
    int r = bar_work(&stuff);
    return bar_cleanup(r, &stuff);
}
bản chất "tiềm ẩn" dọn sạch từ điểm nhìn của function thực hiện các công việc có thể xem thuận lợi của một số. Một số sưng lên code tiềm năng cũng là tránh được chỉ gọi function dọn dẹp từ một nơi duy nhất. Một số người cho rằng hành vi "tiềm ẩn" là "khó khăn", và do đó khó khăn hơn để hiểu và duy trì.

linh tinh...

giải pháp bí truyền hơn bằng cách sử dụng setjmp() / longjmp() có thể được xem xét, nhưng sử dụng chúng một cách chính xác có thể được khó khăn. Có hàm bao mã nguồn mở thực hiện try/catch exception handling kiểu macro qua chúng (ví dụ: cexcept ), nhưng bạn có thể thay đổi phong cách mã hóa của bạn để sử dụng kiểu đó để xử lý error . Ai cũng có thể xem xét việc thực hiện các function như một máy nhà nước. function theo dõi sự tiến bộ qua mỗi tiểu bang, một error gây ra function để ngắn mạch để dọn dẹp nhà nước. Phong cách này thường là reserved cho các chức năng đặc biệt phức tạp, hoặc các chức năng mà cần phải được thử lại sau đó và có thể nhận từ nơi họ rời đi.

làm như người La Mã làm.

nếu bạn cần tuân thủ để mã hóa tiêu chuẩn, sau đó phương pháp tốt nhất là để làm theo bất cứ điều gì kỹ thuật là phổ biến nhất trong code hiện có cơ sở. Điều này áp dụng cho hầu như tất cả các khía cạnh của thực hiện thay đổi đến một code ổn định source hiện có cơ sở. Nó sẽ được coi là gây rối để giới thiệu một phong cách mã hóa mới. Bạn nên tìm kiếm sự chấp thuận của quyền hạn đó được nếu bạn cảm thấy một sự thay đổi đáng kể sẽ cải thiện một số khía cạnh của phần mềm. Nếu không, như là "sang trọng" là chủ quan, tranh cãi vì lợi ích của "thanh lịch" sẽ không giúp bạn có bất cứ nơi nào.
Đã bình luận 06/6/2016 bởi Astatic (170 điểm)
(Theo tôi) của thành ngữ ' thử/cuối cùng' là nó sử dụng syntax để rõ ràng demarcate phần code có thể được tháo ra và làm sạch bằng cách sử dụng 'cuối cùng' khối, trong khi với một label 'goto' hạn chế là chỉ thi hành theo quy ước (nghĩa là bạn không thể ngay lập tức nhìn thấy bằng cách nhìn vào phần đó của code , cho dù có bất kỳ khác 'GOTO' phát biểu gây ra control nhập khối dọn dẹp). Cấp, kể từ khi tuyên bố 'GOTO' không thể nhảy giữa các chức năng, Giữ một sane function tối đa chiều dài hơn hoặc ít hơn obviates vấn đề này.
0 Phiếu
Đã trả lời 04/6/2016 bởi Makesdo (610 điểm)
goto statement không bao giờ là cần thiết, cũng dễ dàng hơn để viết code mà không sử dụng nó. Thay vào đó, bạn có thể sử dụng một function sạch hơn và quay trở lại.
if(exit_cond) {
    clean_the_mess();
    return;
}
hoặc bạn có thể phá vỡ như Vlad đã đề cập ở trên. Nhưng một trong những nhược điểm đó, nếu bạn loop có sâu sắc nested structure phá vỡ chỉ sẽ thoát ra khỏi vòng lặp trong cùng. Ví dụ:
While ( exp ) {
    for (exp; exp; exp) {
        for (exp; exp; exp) {
            if(exit_cond) {
                clean_the_mess();
                break;
            }
        }
    }
}
sẽ chỉ thoát ra từ bên trong cho loop và không từ bỏ quá trình.
Đã bình luận 05/6/2016 bởi n2585 (180 điểm)
Nhưng sang trọng là một vấn đề ý kiến! 'goto' được sử dụng (chăm sóc) trong một số chương trình.
Đã bình luận 05/6/2016 bởi You_One (190 điểm)
Mà sử dụng 'goto' là thực sự vô hại. Đừng quên rằng 'trở lại', 'tiếp tục', 'break' vv là sử dụng chỉ cần lắng của 'goto' trong đó có cú pháp riêng của họ, để ngăn chặn lỗi và làm cho code rõ ràng hơn. Trong case này bạn vừa phát hiện các ' try:... cuối cùng:...' thành ngữ được tìm thấy trong nhiều ngôn ngữ khác. Kể từ khi C không có một cú pháp, 'goto cleanup' là statement kỹ lưỡng hơn cung cấp các ý nghĩa của chiến dịch đó. IMHO thủ thuật 'trong khi (0)' là chỉ một hack, ít hơn nhiều dễ đọc hơn bằng cách sử dụng 'goto'.

ToughDev Q&A là gì?

Trang web hỏi đáp cho các bạn đam mê lập trình, phát triển phần mềm và các vấn đề kỹ thuật khác. Với sự giúp đỡ của bạn, chúng tôi hy vọng sẽ xây dựng thành công một thư viện đầy đủ các câu hỏi và trả lời về tất cả các vấn đề có liên quan đến lập trình!







...