làm {Lôi} trong khi (sai)


125

Tôi đã xem xét một số mã bởi một cá nhân và nhận thấy anh ta dường như có một mẫu trong các chức năng của mình:

<return-type> function(<params>)
{
 <initialization>

 do
 {
   <main code for function>
 }
 while(false);

 <tidy-up & return>
}

Nó không tệ , kỳ dị hơn (mã thực tế khá gọn gàng và không có gì đáng ngạc nhiên). Đó không phải là thứ tôi đã thấy trước đây và tôi tự hỏi liệu có ai có thể nghĩ ra bất kỳ logic nào đằng sau nó không - có lẽ là một ngôn ngữ khác?


2
Bạn đã thử thay đổi nó thành phiên bản "bình thường" và xem liệu nó có còn biên dịch không? Nếu nó không biên dịch khi được viết bình thường, lỗi trình biên dịch có thể đưa ra manh mối về lý do tại sao điều này được thực hiện.
Mark Byers

11
Tại sao không hỏi "cá nhân" thay vì hỏi chúng tôi để đoán ý định của anh ấy?

1
Có lẽ giáo sư của anh ta yêu cầu anh ta sử dụng một do...while()và anh ta đã sử dụng mã đó làm mẫu hàm kể từ đó.
Hogan

6
@Neil ... Tôi muốn tránh bị câm khi đối mặt với anh ấy và anh ấy giải thích cách sử dụng C ++ siêu tiên tiến mà tôi chưa bao giờ nghĩ tới.
Ông Boy

6
Dường như đã có khá nhiều câu trả lời khác nhau, bạn có thể hỏi tác giả ban đầu ý định của họ là gì và đăng lại không?
nevets1219

Câu trả lời:


174

Bạn có thể breakra khỏi do{...}while(false).


95
+1 bởi vì đó có thể là mục đích của mã, nhưng làm một cái gì đó như thế này chỉ là một goto ngụy trang ngu ngốc. Nếu bạn nghĩ rằng một goto là công cụ phù hợp cho công việc, thì bạn chỉ nên sử dụng # $ (* # @ goto.
dsimcha

55
Nó còn hơn cả một chiếc goto trá hình. Nó là một goto hạn chế (có cấu trúc).
Thomas Eding

19
Nó bị "hạn chế" theo cách nào? Chỉ nhảy về phía trước hầu như không phải là một "hạn chế". Một goto là một goto, và mặc quần áo lên để làm cho nó trông giống như nó không tệ hơn là chỉ sử dụng một goto ở nơi đầu tiên.
Anon.

44
@Anon.: Nhảy về phía trước là một hạn chế đối với một chiếc goto, và nhảy ra ngoài chắc chắn là một hạn chế. Vấn đề thực sự với gotos là mã spaghetti, và giới hạn nhảy về phía trước và ra rất nhiều.
David Thornley

33
Một vòng lặp thực tế không phải là một goto. Một điều kiện không phải là một goto. "Đến cuối của hàm và làm mã ngẫu nhiên" ngữ nghĩa một goto. Sử dụng gotos khi ngữ nghĩa được áp dụng, đừng ăn mặc một cái gì đó khác biệt về ngữ nghĩa bởi vì bạn sợ chúng.
Anon.

125

Rất nhiều người chỉ ra rằng nó thường được sử dụng để giải lao như một cách viết "goto" vụng về. Điều đó có thể đúng nếu nó được viết trực tiếp trong hàm.

Trong một macro, OTOH, do { something; } while (false)là một cách thuận tiện để FORCE một dấu chấm phẩy sau khi gọi macro, hoàn toàn không có mã thông báo nào khác được phép theo dõi.

Và một khả năng khác là đã từng có một vòng lặp ở đó hoặc lặp đi lặp lại được dự kiến ​​sẽ được thêm vào trong tương lai (ví dụ: trong phát triển dựa trên thử nghiệm, việc lặp lại không cần thiết để vượt qua các thử nghiệm, nhưng về mặt logic thì sẽ có ý nghĩa lặp lại ở đó nếu chức năng cần thiết để có phần chung hơn so với yêu cầu hiện tại)


18
+1 để đề cập đến tiện ích này trong macro; Tôi ngạc nhiên không ai khác đề cập đến điều đó!
Nick Meyer

7
Đúng, điều vĩ mô thực sự là một cách sử dụng hoàn toàn hợp lệ của điều này. Tất nhiên, bên ngoài các macro, nó chỉ là ngớ ngẩn ...;)
jalf

12
Nó không phải lúng túng, đó là hữu ích, bởi vì đó là một scoped goto - nó có nghĩa là bất kỳ biến bạn khai báo trong vòng lặp do bị phá hủy, trong khi goto không làm điều đó.
Ana Betts

9
@Paul: Không có gì ngăn cản bạn thêm dấu ngoặc nhọn xung quanh một loạt các câu lệnh để buộc hành vi này bằng goto.
erikkallen

5
@Paul: goto ra khỏi một khối chắc chắn khiến các biến cục bộ bị phá hủy trong C ++, giống như phá vỡ. Và trong các biến C không thực sự bị phá hủy, không gian ngăn xếp của chúng chỉ đơn giản là được thu hồi.
Ben Voigt

25

Sự phá vỡ như goto có lẽ là câu trả lời, nhưng tôi sẽ đưa ra một ý tưởng khác.

Có lẽ anh ta muốn có một biến được xác định cục bộ và sử dụng cấu trúc này để có một phạm vi mới.

Hãy nhớ rằng trong khi C ++ gần đây cho phép {...}mọi nơi, điều này không phải lúc nào cũng đúng.


30
Anh ta chỉ có thể sử dụng niềng răng xoăn trong trường hợp đó.
Nemanja Trifunovic

2
@Nemanja, bạn sẽ ngạc nhiên khi có nhiều nhà phát triển không biết điều đó và thử một cái gì đó liên quan đến những gì Hogan đang đề xuất
Polaris878

8
@Polaris @Nemanja, chưa đến lúc tôi đã lập trình trong C / C ++ như 4 năm mà tôi nhận ra rằng bạn có thể tạo một phạm vi cục bộ mới ở {} bất cứ đâu .. Điều này đặc biệt tiện dụng trong switch-case
Earlz 22/210

1
@Nemanja: Tôi không biết chính xác khi nào, nhưng tôi chắc rằng {...}bất cứ nơi nào là sự phát triển hiện đại hơn trong C ++ (hãy nhớ rằng C ++ đầu tiên tôi sử dụng được thực hiện với bộ xử lý trước và điều đó không cho phép sử dụng hiện đại này.) Có thể tác giả chỉ là trường học cũ.
Hogan

2
Khi nào nó không cho phép niềng răng ở khắp mọi nơi? Tôi đã bắt đầu lập trình như 15 năm trước, và nó đã được cho phép sau đó (cả trong sách giáo khoa của tôi và trong mọi trình biên dịch tôi đã thử).
erikkallen

20

Tôi đã thấy nó được sử dụng như một mẫu hữu ích khi có nhiều điểm thoát tiềm năng cho hàm, nhưng mã dọn dẹp tương tự luôn được yêu cầu bất kể hàm thoát ra như thế nào.

Nó có thể làm cho mệt mỏi nếu / khác - nếu cây dễ đọc hơn rất nhiều, bằng cách chỉ cần ngắt bất cứ khi nào đạt đến điểm thoát, với phần còn lại của logic logic sau đó.

Mẫu này cũng hữu ích trong các ngôn ngữ không có câu lệnh goto. Có lẽ đó là nơi lập trình viên ban đầu học mô hình.


15
Sau đó, chỉ cần sử dụng một goto trung thực, đơn giản thay vì một goto ngụy trang mỏng.
dsimcha

4
Tôi thích cách này tốt hơn. Nó rất dễ đọc và không mang theo sự kỳ thị của một chiếc goto.
Cameron

8
gotos hoàn toàn dễ đọc nếu được sử dụng một cách tiết kiệm và cục bộ. Họ có sự kỳ thị từ phía sau khi họ là hình thức chính của kiểm soát dòng chảy và đã nhảy qua hàng trăm dòng.
dsimcha

13
Không sử dụng goto vì "sự kỳ thị" là một dấu hiệu chắc chắn về lập trình sùng bái hàng hóa.
Anon.

6
Chưa kể rằng một khối mã dọn dẹp là một mùi hôi. Đây là C ++, mã dọn dẹp thường nằm trong các hàm hủy được gọi trong quá trình xử lý RAII.
David Thornley

12

Tôi đã thấy mã như vậy để bạn có thể sử dụng breaknhư một gotoloại.


10

Đây chỉ là một sự đồi trụy whileđể có được các sơ đồ goto tidy-upmà không sử dụng từ goto.

Đó là hình thức xấu bởi vì khi bạn sử dụng các vòng lặp khác bên ngoài while, nó breakstrở nên mơ hồ với người đọc. "Đây có phải là để thoát khỏi goto không? Hay điều này chỉ nhằm thoát ra khỏi vòng lặp bên trong?"


10

Tôi nghĩ rằng nó thuận tiện hơn để viết breakthay vì goto end. Bạn thậm chí không phải nghĩ ra một tên cho nhãn làm cho ý định rõ ràng hơn: Bạn không muốn chuyển sang một nhãn với một tên cụ thể. Bạn muốn ra khỏi đây.

Ngoài ra cơ hội là bạn sẽ cần niềng răng nào. Vì vậy, đây là do{...}while(false);phiên bản:

do {
   // code
   if (condition) break; // or continue
   // more code
} while(false);

Và đây là cách bạn sẽ phải thể hiện nó nếu bạn muốn sử dụng goto:

{
   // code
   if (condition) goto end;
   // more code
}
end:

Tôi nghĩ rằng ý nghĩa của phiên bản đầu tiên dễ nắm bắt hơn nhiều. Ngoài ra, nó dễ viết hơn, dễ mở rộng hơn, dễ dịch sang ngôn ngữ không hỗ trợ goto, v.v.


Mối quan tâm được đề cập thường xuyên nhất về việc sử dụng breaklà nó được ngụy trang rất tệ goto. Nhưng thực sự breakcó nhiều điểm tương đồng với return: Cả hai hướng dẫn đều nhảy ra khỏi một khối mã được cấu trúc khá nhiều so với goto. Tuy nhiên, cả hai hướng dẫn đều cho phép nhiều điểm thoát trong một khối mã đôi khi có thể gây nhầm lẫn. Sau tất cả, tôi sẽ cố gắng đưa ra giải pháp rõ ràng nhất, bất cứ điều gì trong tình huống cụ thể.


Ồ, tôi chỉ nhận thấy rằng tôi đã không hiểu câu hỏi hoàn toàn trước khi trả lời. Tôi nghĩ rằng đó là về việc sử dụng do{ ... }while(false);nói chung. Nhưng thực ra đó là về việc sử dụng nó để mô phỏng một số loại try{ ... }finally{ ... }.
Robert

Quan sát tốt đẹp về sự song song giữa phá vỡ và trở lại chứ không phải là goto. Đồng ý rằng giải pháp rõ ràng nhất là tốt nhất. Trong nhiều trường hợp, có thể rõ ràng hơn để đóng gói mã như thế này trong chức năng riêng của nó sử dụng trả về thay vì ngắt và sau đó thực hiện dọn dẹp trong chức năng gọi.
thuyết phục

7

Thủ thuật này được sử dụng bởi các lập trình viên quá ngại sử dụng một gotomã rõ ràng trong mã của họ. Tác giả của đoạn mã trên muốn có khả năng nhảy trực tiếp đến điểm "dọn dẹp và trả lại" từ giữa đoạn mã. Nhưng họ không muốn sử dụng nhãn hiệu và rõ ràng goto. Thay vào đó, họ có thể sử dụng breakbên trong cơ thể của chu kỳ "giả" ở trên để đạt được hiệu quả tương tự.


7

Đó là một thực tế rất phổ biến. Trong C . Tôi cố gắng nghĩ về nó như thể bạn muốn nói dối chính mình theo cách "Tôi không sử dụng goto". Nghĩ về nó, sẽ không có gì sai với việc gotosử dụng tương tự. Trong thực tế, nó cũng sẽ làm giảm mức độ thụt.

Điều đó nói rằng, mặc dù, tôi nhận thấy, rất thường xuyên do..whilecác vòng lặp này có xu hướng phát triển. Và sau đó họ nhận được ifs và elses bên trong, khiến cho mã thực sự không thể đọc được, chứ đừng nói là có thể kiểm tra được.

Những người do..whilethường được dự định để làm sạch . Bằng mọi cách có thể, tôi thích sử dụng RAIIquay lại sớm từ một chức năng ngắn . Mặt khác, C không cung cấp cho bạn nhiều tiện ích như C ++ , tạo ra một do..whiletrong những cách tiếp cận tốt nhất để dọn dẹp.


6

Nó trông giống như một lập trình viên C. Trong C ++, các biến tự động có các hàm hủy mà bạn sử dụng để dọn dẹp, do đó không nên dọn dẹp trước khi trả về. Trong C, bạn không có thành ngữ RAII này , vì vậy nếu bạn có mã dọn dẹp chung, bạn có thể gotosử dụng nó hoặc sử dụng vòng lặp một lần như trên.

Nhược điểm chính của nó so với thành ngữ C ++ là nó sẽ không gọn gàng nếu một ngoại lệ được ném vào cơ thể. C không có ngoại lệ, vì vậy đây không phải là vấn đề, nhưng nó khiến nó trở thành thói quen xấu trong C ++.


4

Một số giải thích. Cái đầu tiên là chung, cái thứ hai là dành riêng cho các macro tiền xử lý C với các tham số:

Kiểm soát lưu lượng

Tôi đã thấy điều này được sử dụng trong mã C đơn giản. Về cơ bản, đây là phiên bản an toàn hơn của goto, vì bạn có thể thoát ra khỏi nó và tất cả bộ nhớ được dọn sạch đúng cách.

Tại sao một cái gì đó gotogiống như là tốt? Vâng, nếu bạn có mã nơi khá nhiều mỗi dòng có thể trả về một lỗi, nhưng bạn cần phải phản ứng với tất cả chúng cùng một cách (ví dụ bằng cách bàn giao các lỗi để người gọi của bạn sau khi dọn dẹp), nó thường dễ đọc hơn để tránh if( error ) { /* cleanup and error string generation and return here */ }như nó tránh sự trùng lặp của mã dọn dẹp.

Tuy nhiên, trong C ++, bạn có ngoại lệ + RAII cho chính xác mục đích này, vì vậy tôi sẽ coi đó là phong cách mã hóa xấu.

Kiểm tra dấu chấm phẩy

Nếu bạn quên dấu chấm phẩy sau khi gọi macro giống như hàm, các đối số có thể co lại theo cách không mong muốn và biên dịch thành cú pháp hợp lệ. Tưởng tượng vĩ mô

#define PRINT_IF_DEBUGMODE_ON(msg) if( gDebugModeOn ) printf("foo");

Điều đó vô tình được gọi là

if( foo )
    PRINT_IF_DEBUGMODE_ON("Hullo\n")
else
    doSomethingElse();

Các "khác" sẽ được coi là liên quan đến việc gDebugModeOn, vì vậy khi foofalse, chính xác đảo ngược những gì đã được dự định sẽ xảy ra.

Cung cấp một phạm vi cho các biến tạm thời.

Vì do / while có dấu ngoặc nhọn, các biến tạm thời có phạm vi được xác định rõ ràng mà chúng không thể thoát.

Tránh các cảnh báo "có thể không mong muốn"

Một số macro chỉ được kích hoạt trong các bản dựng gỡ lỗi. Bạn định nghĩa chúng như:

#if DEBUG
#define DBG_PRINT_NUM(n) printf("%d\n",n);
#else
#define DBG_PRINT_NUM(n) 
#endif

Bây giờ nếu bạn sử dụng điều này trong một bản phát hành bên trong một điều kiện, nó sẽ biên dịch thành

if( foo )
    ;

Nhiều trình biên dịch xem điều này giống như

if( foo );

Mà thường được viết vô tình. Vì vậy, bạn nhận được một cảnh báo. Do {} while (false) ẩn điều này khỏi trình biên dịch và được nó chấp nhận như một dấu hiệu cho thấy bạn thực sự không muốn làm gì ở đây.

Tránh bắt các dòng bởi các điều kiện

Macro từ ví dụ trước:

if( foo )
    DBG_PRINT_NUM(42)
doSomething();

Bây giờ, trong một bản dựng gỡ lỗi, vì chúng ta cũng thường bao gồm dấu chấm phẩy, điều này biên dịch tốt. Tuy nhiên, trong bản phát hành, bản dựng này đột nhiên biến thành:

if( foo )

doSomething();

Hoặc được định dạng rõ ràng hơn

if( foo )
    doSomething();

Đó không phải là tất cả những gì đã được dự định. Việc thêm một {...} while (false) xung quanh macro sẽ biến dấu chấm phẩy bị thiếu thành một lỗi biên dịch.

Điều đó có nghĩa gì với OP?

Nói chung, bạn muốn sử dụng các ngoại lệ trong C ++ để xử lý lỗi và các mẫu thay vì macro. Tuy nhiên, trong trường hợp rất hiếm khi bạn vẫn cần macro (ví dụ: khi tạo tên lớp bằng cách sử dụng dán mã thông báo) hoặc bị giới hạn ở đồng bằng C, đây là một mẫu hữu ích.


Về phạm vi, bạn không cần bộ máy vòng lặp; một khối "không được trang bị" là hợp pháp : x =1; y = 2; { int tmp = y; y = x; x = tmp; }.
chepner

Mặc dù vậy, một khối không được trang bị không thực thi sự hiện diện của dấu chấm phẩy bị thiếu, điều này có thể có tác dụng phụ không mong muốn. Làm trong khi(); YÊU CẦU dấu chấm phẩy và do đó, ví dụ: Không gây ra các câu lệnh sau được rút ra thành "nếu" nếu macro xác định không có gì trong bản dựng phát hành, như ví dụ của tôi ở trên.
uliwitness

Không thể tránh điều đó bằng cách đơn giản là cung cấp một định nghĩa tốt hơn cho macro? #define DBG_PRINT_NUM(n) {}.
chepner

1
Không, vì {} là một câu lệnh đầy đủ, tức là tương đương với dấu ";". Vì vậy, nếu bạn viết if(a) DBG_PRINT_NUM(n); else other();Nó sẽ không biên dịch. Chỉ if(a) {} else other();hoặc if(a) ; else other();là hợp lệ, nhưng if(a) {}; else other();không, bởi vì điều đó làm cho mệnh đề "nếu" bao gồm hai câu lệnh.
uliwitness

2

Có lẽ nó được sử dụng để breakcó thể được sử dụng bên trong để hủy bỏ việc thực thi mã tiếp theo tại bất kỳ thời điểm nào:

do {
    if (!condition1) break;
    some_code;
    if (!condition2) break;
    some_further_code;
    // …
} while(false);

7
Làm như vậy có vẻ như là một nỗ lực để tránh sử dụng gotochỉ vì ai đó nghe thấy nó "xấu".
Anon.

1
Có lẽ, nhưng C ++ có ngoại lệ cho chính lý do này.
TED

2

Tôi nghĩ rằng điều này được thực hiện để sử dụng breakhoặc continuebáo cáo. Một số loại mã "goto".


2

Thật đơn giản: Rõ ràng bạn có thể nhảy ra khỏi vòng lặp giả bất cứ lúc nào bằng cách sử dụng breakcâu lệnh. Hơn nữa, dokhối là một phạm vi riêng biệt (cũng có thể đạt được { ... }chỉ với ).

Trong tình huống như vậy, có thể nên sử dụng RAII (các đối tượng tự động hủy chính xác khi hàm kết thúc). Một cấu trúc tương tự khác là việc sử dụng goto- vâng, tôi biết điều đó là xấu , nhưng nó có thể được sử dụng để có mã dọn dẹp chung như vậy:

<return-type> function(<params>)
{
 <initialization>

 <main code for function using "goto error;" if something goes wrong>

 <tidy-up in success case & return>

 error:

 <commmon tidy-up actions for error case & return error code or throw exception>
}

(Như một bên: Cấu trúc do-while-false được sử dụng trong Lua để đưa ra continuetuyên bố bị thiếu .)


2

Nhiều người trả lời đã đưa ra lý do cho do{(...)break;}while(false). Tôi muốn bổ sung cho bức tranh bằng một ví dụ thực tế khác.

Trong đoạn mã sau tôi phải đặt liệt kê operationdựa trên địa chỉ được datatrỏ bởi con trỏ. Bởi vì trường hợp chuyển đổi chỉ có thể được sử dụng trên các loại vô hướng nên tôi đã làm nó không hiệu quả theo cách này

if (data == &array[o1])
    operation = O1;
else if (data == &array[o2])
    operation = O2;
else if (data == &array[on])
    operation = ON;

Log("operation:",operation);

Nhưng vì Log () và phần còn lại của mã lặp lại cho bất kỳ giá trị hoạt động được chọn nào, tôi đã đi lang thang làm thế nào để bỏ qua phần so sánh còn lại khi địa chỉ đã được phát hiện. Và đây là nơi do{(...)break;}while(false)có ích.

do {
    if (data == &array[o1]) {
        operation = O1;
        break;
    }
    if (data == &array[o2]) {
        operation = O2;
        break;
    }
    if (data == &array[on]) {
        operation = ON;
        break;
    }
} while (false);

Log("operation:",operation);

Người ta có thể tự hỏi tại sao anh ta không thể làm điều tương tự breaktrong một iftuyên bố, như:

if (data == &array[o1])
{
    operation = O1;
    break;
}
else if (...)

breaktương tác hoàn toàn với kèm theo gần nhất vòng lặp hoặc switch, cho dù đó là một for, whilehoặc do .. whileloại, vì vậy không may rằng sẽ không làm việc.


1

Tác giả bao nhiêu tuổi

Tôi hỏi bởi vì tôi đã từng bắt gặp một số mã Fortran thời gian thực đã làm điều đó, vào cuối những năm 80. Hóa ra đó là một cách thực sự tốt để mô phỏng các luồng trên HĐH mà không có chúng. Bạn chỉ cần đặt toàn bộ chương trình (trình lập lịch biểu của bạn) vào một vòng lặp và gọi từng "thói quen" luồng "của mình. Các luồng thường là các vòng lặp lặp đi lặp lại cho đến khi một trong số các điều kiện xảy ra (thường là một số lượng nhất định Thời gian đã trôi qua) Đó là "đa nhiệm hợp tác", theo đó, tùy thuộc vào các luồng riêng lẻ để từ bỏ CPU mọi lúc và sau đó các luồng khác không bị bỏ đói. Bạn có thể lồng các lệnh gọi chương trình con lặp để mô phỏng mức độ ưu tiên của luồng các ban nhạc.



0

Tôi đồng ý với hầu hết các áp phích về việc sử dụng như một chiếc goto được ngụy trang mỏng. Macro cũng đã được đề cập như một động lực tiềm năng để viết mã theo phong cách.

Tôi cũng đã thấy cấu trúc này được sử dụng trong các môi trường C / C ++ hỗn hợp như một ngoại lệ của một người nghèo. Có thể sử dụng "do {} while (false)" với "break" để bỏ qua đến cuối khối mã nếu có gì đó thường đảm bảo sẽ gặp phải ngoại lệ trong vòng lặp.

Tôi cũng đã xây dựng cấu trúc này được sử dụng trong các cửa hàng nơi hệ tư tưởng "lợi nhuận duy nhất cho mỗi chức năng" được thi hành. Một lần nữa, điều này thay cho một "goto" rõ ràng - nhưng động lực là để tránh nhiều điểm trả về, không "bỏ qua" mã và tiếp tục thực thi thực tế trong chức năng đó.


0

Tôi làm việc với Adobe InDesign SDK và các ví dụ SDK InDesign có hầu hết mọi chức năng được viết như thế này. Đó là do thực tế là chức năng thường thực sự dài. Nơi bạn cần thực hiện QueryInterface (...) để lấy bất cứ thứ gì từ mô hình đối tượng ứng dụng. Vì vậy, thông thường mọi QueryInterface được theo sau bởi ifkhông hoạt động tốt, phá vỡ.


0

Nhiều người đã tuyên bố sự tương đồng giữa cấu trúc này và a goto, và bày tỏ sự ưa thích đối với goto. Có lẽ nền tảng của người này bao gồm một môi trường nơi goto bị nghiêm cấm bởi các nguyên tắc mã hóa?


0

Một lý do khác mà tôi có thể nghĩ đến là nó trang trí niềng răng, trong khi tôi tin rằng một niềng răng trần tiêu chuẩn C ++ mới hơn không ổn (ISO C không thích chúng). Nếu không để yên một máy phân tích tĩnh như lint.

Không chắc chắn tại sao bạn muốn chúng, có thể là phạm vi thay đổi hoặc lợi thế với trình gỡ lỗi.

Xem vòng lặp Trivial Do WhileNiềng răng tốt từ C2.

Để làm rõ thuật ngữ của tôi (mà tôi tin là tuân theo cách sử dụng tiêu chuẩn):

Niềng răng trần :

init();
...
{
c = NULL;
mkwidget(&c);
finishwidget(&c);
}
shutdown();

Niềng răng trống (NOP):

{}

ví dụ

while (1)
   {}  /* Do nothing, endless loop */

Khối :

if (finished)
{
     closewindows(&windows);
     freememory(&cache);
}

mà sẽ trở thành

if (finished)
     closewindows(&windows);
freememory(&cache);

nếu các dấu ngoặc được loại bỏ, do đó thay đổi luồng thực hiện, không chỉ phạm vi của các biến cục bộ. Do đó, không "tự do" hay "trần trụi".

Niềng răng trần hoặc khối có thể được sử dụng để biểu thị bất kỳ phần mã nào có thể là tiềm năng cho hàm (nội tuyến) mà bạn muốn đánh dấu, nhưng không được cấu trúc lại tại thời điểm đó.


Có thật không? Thật là sự xấu hổ. Một trong số ít những điều tôi thích về C là nó cho phép bạn khai báo một phạm vi lồng nhau mới ở bất cứ đâu.
TED

1
Niềng răng đôi khi rất hữu ích để khắc phục sự cố kỳ lạ. Xem blog.msdn.com/oldnewthing/archive/2004/05/20/135841.aspx .
Brian

1
Trong C ++, một khối (tức là "niềng răng trần" của bạn) có thể được sử dụng ở bất cứ đâu mà một câu lệnh sẽ được cho phép.
Ben Voigt

@BenVoigt Niềng răng trống tức là NOP khác với "niềng răng trần" là một khối được thêm vào xung quanh một chuỗi hướng dẫn tuyến tính. Ví dụ: `printf (" Xin chào "); {putar (','); putar (0x20); } printf ("world! \ n"); `trong đó các dấu ngoặc không phải là một phần của điều khiển vòng lặp hoặc nhánh.
mctylr

@mctylr: Tôi không nói về niềng răng trống.
Ben Voigt

0

Đó là một cách giả tạo để mô phỏng GOTOvì hai cái này giống hệt nhau trên thực tế:

// NOTE: This is discouraged!
do {
    if (someCondition) break;
    // some code be here
} while (false);
// more code be here

và:

// NOTE: This is discouraged, too!
if (someCondition) goto marker;
// some code be here
marker:
// more code be here

Mặt khác, cả hai điều này thực sự nên được thực hiện với ifs:

if (!someCondition) {
    // some code be here
}
// more code be here

Mặc dù việc lồng có thể trở nên hơi xấu nếu bạn chỉ biến một chuỗi dài về phía trước GOTOthành ifs lồng nhau . Câu trả lời thực sự là tái cấu trúc thích hợp, mặc dù, không bắt chước các cấu trúc ngôn ngữ cổ xưa.

Nếu bạn đang cố gắng chuyển ngữ một thuật toán với GOTOs trong đó, bạn có thể làm điều đó với thành ngữ này. Tuy nhiên, đó chắc chắn là không chuẩn và là một chỉ báo tốt cho thấy bạn không tuân thủ chặt chẽ các thành ngữ dự kiến ​​của ngôn ngữ.

Tôi không nhận thức được bất kỳ ngôn ngữ giống như C trong đó thực sự là một giải pháp thành ngữ cho bất cứ điều gì.

Bạn có thể có thể cấu trúc lại toàn bộ mớ hỗn độn thành một cái gì đó hợp lý hơn để làm cho nó thành ngữ hơn và dễ đọc hơn nhiều.


1
Alan, do..while(false)không cho chạy "undefined" số lần, nó là để chạy một lần .
Dmitry

2
Nó đang chạy một số lần không xác định nếu bạn có một continuecâu lệnh có điều kiện ở cuối như tôi đã trình bày. Bởi undefinedtôi chỉ đơn giản là bạn không biết liệu nó có chạy nhiều lần hay không, trừ khi bạn có thể dự đoán liệu các điều kiện sẽ được đáp ứng ở một lần lặp cụ thể. Không có bất kỳ continues nào trong đó, do..while(false)sẽ chạy một lần, nhưng cũng không có bất kỳ breaks nào trong đó, while(true)sẽ chạy mãi mãi, vì vậy "hành vi mặc định" không thực sự phù hợp để hiểu những gì có thể được thực hiện với các vòng lặp.
Alan Plum

Nó hữu ích khi bạn xác định một macro có nhiều câu lệnh. Nếu bạn bọc chúng trong một do / while (false), bạn có thể sử dụng macro trong câu lệnh if / other như bất kỳ lệnh gọi hàm nào khác. xem noveltheory.com/TechPapers/ while.htm để được giải thích
John Paquin

5
Ai đó không hiểu ngữ nghĩa của continue. continueKHÔNG lặp lại vòng lặp. "Câu continuelệnh sẽ chỉ xảy ra trong một câu lệnh lặp và làm cho điều khiển chuyển đến phần tiếp tục vòng lặp của câu lệnh lặp đi kèm nhỏ nhất , nghĩa là đến cuối vòng lặp."
Ben Voigt

-1, hai câu lệnh trên không giống nhau vì những lý do mà @BenVoigt chỉ ra.
cmh

0

Một số lập trình viên thích chỉ có một lối ra / trả lại duy nhất từ ​​các chức năng của họ. Việc sử dụng một hình nộm làm {....} while (false); cho phép bạn "thoát ra" khỏi vòng lặp giả sau khi bạn hoàn thành và vẫn có một lần quay lại.

Tôi là một lập trình viên java, vì vậy ví dụ của tôi sẽ giống như

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class p45
{
    static List<String> cakeNames = Arrays.asList("schwarzwald torte", "princess", "icecream");
    static Set<Integer> forbidden = Stream.of(0, 2).collect(Collectors.toSet());

    public static  void main(String[] argv)
    {
        for (int i = 0; i < 4; i++)
        {
            System.out.println(String.format("cake(%d)=\"%s\"", i, describeCake(i)));
        }
    }


    static String describeCake(int typeOfCake)
    {
        String result = "unknown";
        do {
            // ensure type of cake is valid
            if (typeOfCake < 0 || typeOfCake >= cakeNames.size()) break;

            if (forbidden.contains(typeOfCake)) {
                result = "not for you!!";
                break;
            }

            result = cakeNames.get(typeOfCake);
        } while (false);
        return result;
    }
}


-1

Điều này thật thú vị. Có lẽ có những phá vỡ trong vòng lặp như những người khác đã nói. Tôi sẽ làm theo cách này:

while(true)
{
   <main code for function>
   break; // at the end.
}

2
Với tiềm năng lặp lại mãi mãi? do..while(false)thoát ra luôn, while(true)là nhiều hơn về một mặt rủi ro.
Dmitry

1
while(true)là thành ngữ đúng trong hầu hết các ngôn ngữ. Bạn sẽ thường thấy nó trong các ứng dụng GUI là vòng lặp chính của chương trình. Bởi vì về cơ bản, bạn cho rằng chương trình không nên chết cho đến khi được yêu cầu làm như vậy, nên do..while(false)sẽ gây ra tất cả các loại logic. Cách tiếp cận này có thể có nhiều rủi ro hơn từ một POV cầu toàn, nhưng nó dễ dàng hơn về mặt ngữ nghĩa và do đó ít xảy ra lỗi cho các lập trình viên của con người (xin lỗi, Skynet).
Alan Plum

2
@dmitry do{...}while(false)hoàn toàn giống vớiwhile(true){ .. break;}
N 1.1

4
@ N1.1: Không có mặt continue, chúng không giống nhau.
Ben Voigt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.