Tại sao #define TRUE (1 == 1) trong macro boolean C thay vì chỉ đơn giản là 1?


160

Tôi đã thấy định nghĩa trong C

#define TRUE (1==1)
#define FALSE (!TRUE)

Điều này có cần thiết không? Lợi ích gì khi chỉ đơn giản là xác định TRUE là 1 và FALSE là 0?


35
Và hơn thế nữa : #define TRUE (’/’/’/’); #define FALSE (’-’-’-’)(được lấy từ mã hóa-guiances.com/cbook/cbook1_1.pdf trang 871)
osgx

2
Không, đó là sự hoang tưởng bởi sự vô cảm ^ Wunderinformed, thực sự. Trong C, 1 và 0 làm trong mọi trường hợp như nhau.
Jens

@osgx Có nghĩa là gì?
mrgloom

Câu trả lời:


155

Cách tiếp cận này sẽ sử dụng booleanloại thực tế (và giải quyết truefalse) nếu trình biên dịch hỗ trợ nó. (cụ thể là C ++)

Tuy nhiên, sẽ tốt hơn nếu kiểm tra xem C ++ có được sử dụng hay không (thông qua __cplusplusmacro) và thực sự sử dụng truefalse.

Trong một trình biên dịch C, điều này tương đương với 01.
(lưu ý rằng việc xóa dấu ngoặc đơn sẽ phá vỡ điều đó do thứ tự các thao tác)


7
Điều đó không chính xác, bools không được sử dụng ở đây. Kết quả 1==1là một int. (xem stackoverflow.com/questions/7687403/ khăn .)
Mat

4
@Mat: Ngay cả trong C ++, với booleanloại?
SLaks

9
Câu hỏi được gắn thẻ C, nhưng thực sự trong C ++, toán tử quan hệ trả về truehoặc false.
Mat

5
@Mat: Tôi đoán rằng mã như vậy được viết bằng tiêu đề C để chơi tốt với C ++
SLaks

20
@SLaks Nếu nó muốn chơi đẹp với C ++, nó sẽ #define TRUE true#define FALSE falsebất cứ khi nào __cplusplusđược xác định.
Nikos C.

137

Câu trả lời là tính di động. Các giá trị số của TRUEFALSEkhông quan trọng. Có gì quan trọng là một tuyên bố như if (1 < 2)đánh giá lại để if (TRUE)và một tuyên bố như if (1 > 2)đánh giá lại để if (FALSE).

Cấp, trong C, (1 < 2)đánh giá 1(1 > 2)đánh giá 0, như những người khác đã nói, không có sự khác biệt thực tế nào liên quan đến trình biên dịch. Nhưng bằng cách để trình biên dịch xác định TRUEFALSEtheo quy tắc riêng của mình, bạn sẽ làm cho ý nghĩa của chúng rõ ràng với các lập trình viên và bạn đảm bảo tính nhất quán trong chương trình của bạn và bất kỳ thư viện nào khác (giả sử thư viện khác tuân theo tiêu chuẩn C ... bạn kinh ngạc).


Một số Lịch sử
Một số vấn đề cơ bản được xác định FALSEnhư 0TRUEnhư -1. Giống như nhiều ngôn ngữ hiện đại, họ giải thích bất kỳ giá trị khác không là TRUE, nhưng họ đã đánh giá các biểu thức boolean là đúng -1. Hoạt NOTđộng của họ được thực hiện bằng cách thêm 1 và lật ký hiệu, bởi vì nó hiệu quả để làm theo cách đó. Thế là 'KHÔNG x' trở thành -(x+1). Một tác dụng phụ của điều này là một giá trị như 5đánh giá TRUE, nhưng cũng NOT 5đánh giá -6, đó cũng là TRUE! Tìm loại lỗi này không vui.

Thực tiễn tốt nhất
Đưa ra các quy tắc thực tế rằng số 0 được hiểu là FALSEmọi giá trị khác không được hiểu là TRUE, bạn không bao giờTRUEFALSE nên so sánh các biểu thức trông giống boolean với hoặc . Ví dụ:

if (thisValue == FALSE)  // Don't do this!
if (thatValue == TRUE)   // Or this!
if (otherValue != TRUE)  // Whatever you do, don't do this!

Tại sao? Bởi vì nhiều lập trình viên sử dụng phím tắt coi ints là bools. Chúng không giống nhau, nhưng trình biên dịch thường cho phép nó. Vì vậy, ví dụ, nó hoàn toàn hợp pháp để viết

if (strcmp(yourString, myString) == TRUE)  // Wrong!!!

Điều đó có vẻ hợp pháp và trình biên dịch sẽ vui vẻ chấp nhận nó, nhưng có lẽ nó không làm những gì bạn muốn. Đó là bởi vì giá trị trả về strcmp()

      0 nếu yourString == myString
    <0 nếu yourString < myString
    > 0 nếuyourString > myString

Vì vậy, dòng trên TRUEchỉ trả về khi yourString > myString.

Cách đúng để làm điều này là một trong hai

// Valid, but still treats int as bool.
if (strcmp(yourString, myString))

hoặc là

// Better: lingustically clear, compiler will optimize.
if (strcmp(yourString, myString) != 0)

Tương tự:

if (someBoolValue == FALSE)     // Redundant.
if (!someBoolValue)             // Better.
return (x > 0) ? TRUE : FALSE;  // You're fired.
return (x > 0);                 // Simpler, clearer, correct.
if (ptr == NULL)                // Perfect: compares pointers.
if (!ptr)                       // Sleazy, but short and valid.
if (ptr == FALSE)               // Whatisthisidonteven.

Bạn sẽ thường tìm thấy một số "ví dụ xấu" trong mã sản xuất và nhiều lập trình viên có kinh nghiệm đã thề với họ: họ làm việc, một số ngắn hơn các lựa chọn chính xác (về mặt giáo dục?), Và các thành ngữ gần như được công nhận. Nhưng hãy xem xét: các phiên bản "đúng" không kém hiệu quả, chúng được đảm bảo là có thể mang theo được, chúng sẽ vượt qua cả những người nghiêm ngặt nhất và ngay cả những lập trình viên mới cũng sẽ hiểu chúng.

Điều đó có đáng không?


6
(1==1)không di động hơn 1. Các quy tắc riêng của nhà soạn nhạc là các quy tắc của ngôn ngữ C, rõ ràng và không mơ hồ về ngữ nghĩa của các toán tử đẳng thức và quan hệ. Tôi chưa bao giờ thấy một trình biên dịch có được những thứ này sai.
Keith Thompson

1
Trên thực tế, giá trị được trả về strcmpđược biết là nhỏ hơn, bằng hoặc lớn hơn 0. Nó không được đảm bảo là -1, 0 hoặc 1 và có các nền tảng trong tự nhiên không trả lại các giá trị đó để đạt được tốc độ thực hiện. Vì vậy, nếu strcmp(a, b) == TRUEsau đó a > bnhưng ngụ ý ngược lại có thể không giữ.
Maciej Piechotka

2
@KeithThndry - Có lẽ "tính di động" là thuật ngữ sai. Nhưng sự thật vẫn là (1 == 1) là giá trị boolean; 1 thì không.
Adam Liss

2
@AdamLiss: Trong C, (1==1)1cả hai biểu thức không đổi intcó giá trị 1. Chúng giống hệt nhau về mặt ngữ nghĩa. Tôi cho rằng bạn có thể viết mã phục vụ cho những độc giả không biết điều đó, nhưng nó kết thúc ở đâu?
Keith Thompson

2
'không' 5 thực sự là -6, ở mức bit.
woliveirajr

51

Thủ (1 == 1)thuật này rất hữu ích để xác định TRUEtheo cách trong suốt đối với C, nhưng cung cấp cách gõ tốt hơn trong C ++. Mã tương tự có thể được hiểu là C hoặc C ++ nếu bạn đang viết bằng một phương ngữ gọi là "Clean C" (biên dịch thành C hoặc C ++) hoặc nếu bạn đang viết các tệp tiêu đề API có thể được sử dụng bởi các lập trình viên C hoặc C ++.

Trong các đơn vị dịch C, 1 == 1có ý nghĩa chính xác như 1; và 1 == 0có cùng ý nghĩa như 0. Tuy nhiên, trong các đơn vị dịch C ++, 1 == 1có loại bool. Vì vậy, TRUEmacro xác định theo cách đó tích hợp tốt hơn vào C ++.

Một ví dụ về cách nó tích hợp tốt hơn là ví dụ nếu hàm foocó quá tải cho intboolsau đó, thì foo(TRUE)sẽ chọn boolquá tải. Nếu TRUEchỉ được định nghĩa là 1, thì nó sẽ không hoạt động tốt trong C ++. foo(TRUE)sẽ muốn intquá tải.

Tất nhiên, C99 giới thiệu bool, truefalsevà chúng có thể được sử dụng trong các tập tin tiêu đề mà làm việc với C99 và với C.

Tuy nhiên:

  • thực hành xác định TRUEFALSEnhư (0==0)(1==0)trước C99.
  • vẫn còn những lý do chính đáng để tránh xa C99 và làm việc với C90.

Nếu bạn đang làm việc trong một C hỗn hợp và dự án C ++, và không muốn C99, xác định các chữ thường true, falseboolđể thay thế.

#ifndef __cplusplus
typedef int bool;
#define true (0==0)
#define false (!true)
#endif

Điều đó đang được nói, 0==0mẹo là (được?) Được sử dụng bởi một số lập trình viên ngay cả trong mã không bao giờ có ý định tương tác với C ++ theo bất kỳ cách nào. Điều đó không mua bất cứ thứ gì và gợi ý rằng lập trình viên có sự hiểu lầm về cách thức hoạt động của booleans trong C.


Trong trường hợp giải thích C ++ không rõ ràng, đây là một chương trình thử nghiệm:

#include <cstdio>

void foo(bool x)
{
   std::puts("bool");  
}

void foo(int x)
{
   std::puts("int");  
}

int main()
{
   foo(1 == 1);
   foo(1);
   return 0;
}

Đầu ra:

bool
int

Đối với câu hỏi từ các ý kiến ​​về cách các hàm C ++ bị quá tải có liên quan đến lập trình C và C ++ hỗn hợp. Đây chỉ là minh họa một sự khác biệt loại. Một lý do hợp lệ cho việc muốn một truehằng số được boolbiên dịch thành C ++ là để chẩn đoán sạch. Ở các mức cảnh báo cao nhất, trình biên dịch C ++ có thể cảnh báo chúng ta về chuyển đổi nếu chúng ta chuyển một số nguyên dưới dạng booltham số. Một lý do để viết trong Clean C không chỉ là mã của chúng tôi dễ mang theo hơn (vì nó được hiểu bởi trình biên dịch C ++, không chỉ trình biên dịch C), mà chúng ta có thể hưởng lợi từ ý kiến ​​chẩn đoán của trình biên dịch C ++.


3
Tuyệt vời, và đánh giá thấp, trả lời. Rõ ràng là hai định nghĩa về TRUEý chí sẽ khác nhau theo C ++.
dùng4815162342

4
Làm thế nào là các hàm quá tải có liên quan đến mã biên dịch cả C và C ++?
Keith Thompson

@KeithThndry Không chỉ nói về quá tải mà nói chung là gõ đúng cách, quá tải chỉ là ví dụ thực tế nhất khi phát huy tác dụng. Tất nhiên mã C ++ không bị quá tải, các mẫu và tất cả những thứ "phức tạp" được loại bỏ cho "Tương thích C" hoàn toàn không quan tâm nhiều đến các loại, nhưng điều đó không có nghĩa là người ta nên lật đổ các giới hạn loại khái niệm trong một ngôn ngữ nhất định .
Christian Rau

1
@ChristianRau: Ý bạn là gì "không thực sự quan tâm nhiều đến các loại"? Các loại là trung tâm của ngôn ngữ C; mọi biểu thức, giá trị và đối tượng trong chương trình C đều có kiểu được xác định rõ. Nếu bạn muốn định nghĩa một cái gì đó khác nhau trong C và trong C ++ (trong những trường hợp hiếm hoi mà bạn thực sự cần phải viết mã biên dịch thành cả C và C ++), bạn có thể sử dụng #ifdef __cplusplusđể thể hiện ý định của mình rõ ràng hơn nhiều.
Keith Thompson

@KeithThndry Có tôi biết các loại quan trọng như thế nào. Chỉ là không có tất cả những thứ nhận biết kiểu như quá tải và mẫu, những thứ như sự khác biệt giữa boolintkhông quan trọng trong thực tế, vì chúng hoàn toàn có thể chuyển đổi lẫn nhau (và trong C thực sự "giống nhau" , lưu ý các trích dẫn , mặc dù) và không có nhiều tình huống mà bạn thực sự cần phải phân biệt giữa hai. "không nhiều" có lẽ là quá nặng, "ít hơn nhiều so với mã sử dụng các mẫu và quá tải" có lẽ sẽ tốt hơn.
Christian Rau

18
#define TRUE (1==1)
#define FALSE (!TRUE)

tương đương với

#define TRUE  1
#define FALSE 0

trong C.

Kết quả của các toán tử quan hệ là 0hoặc 1. 1==1được đảm bảo để được đánh giá 1!(1==1)được đảm bảo để được đánh giá 0.

Hoàn toàn không có lý do để sử dụng mẫu đầu tiên. Lưu ý rằng biểu mẫu đầu tiên tuy nhiên không kém hiệu quả vì trên hầu hết các trình biên dịch, một biểu thức hằng được đánh giá tại thời gian biên dịch thay vì tại thời gian chạy. Điều này được cho phép theo quy tắc này:

(C99, 6.6p2) "Một biểu thức hằng có thể được đánh giá trong quá trình dịch thay vì thời gian chạy và do đó có thể được sử dụng ở bất kỳ nơi nào có thể có hằng số."

PC-Lint thậm chí sẽ phát hành một thông báo (506, boolean giá trị không đổi) nếu bạn không sử dụng một chữ TRUEFALSEmacro:

Đối với C, TRUEnên được xác định là 1. Tuy nhiên, các ngôn ngữ khác sử dụng số lượng khác 1, vì vậy một số lập trình viên cảm thấy rằng !0chơi an toàn.

Cũng trong C99, các stdbool.hđịnh nghĩa cho macro boolean truefalse sử dụng trực tiếp bằng chữ:

#define true   1
#define false  0

1
Tôi có nghi ngờ, TRUE được thay thế ở mỗi lần sử dụng với 1 == 1, trong khi chỉ cần sử dụng 1 sẽ thay thế 1, không phải là phương pháp đầu tiên so sánh thêm chi phí ... hay nó được tạo ra trình biên dịch tối ưu hóa?
Pinkpanther

4
Các biểu thức hằng @pinkpanther thường được đánh giá tại thời gian biên dịch và do đó không gây ra bất kỳ chi phí nào.
ouah

2
1==1được đảm bảo để được đánh giá1
ouah

3
@NikosC. Đó là một câu hỏi hay. Điều này quan trọng đối với mã của biểu mẫu if(foo == true), sẽ đi từ thực tiễn xấu đến lỗi thẳng.
djechlin

1
+1 để chỉ ra những nguy hiểm trong (x == TRUE)có thể có giá trị thật khác hơn x.
Joshua Taylor

12

Ngoài C ++ (đã được đề cập), một lợi ích khác là cho các công cụ phân tích tĩnh. Trình biên dịch sẽ loại bỏ bất kỳ sự thiếu hiệu quả nào, nhưng một bộ phân tích tĩnh có thể sử dụng các loại trừu tượng của riêng nó để phân biệt giữa các kết quả so sánh và các loại số nguyên khác, do đó, nó biết rằng TRUE phải là kết quả của một so sánh và không nên được coi là tương thích với một số nguyên.

Rõ ràng C nói rằng chúng tương thích, nhưng bạn có thể chọn cấm sử dụng tính năng đó một cách có chủ ý để giúp làm nổi bật các lỗi - ví dụ, trong đó ai đó có thể nhầm lẫn &&&, hoặc họ đã làm hỏng ưu tiên nhà điều hành của họ.


1
Đó là một điểm tốt và có thể một số công cụ này thậm chí có thể bắt được mã ngớ ngẩn if (boolean_var == TRUE) bằng cách mở rộng if (boolean_var == (1 == 1))nhờ vào thông tin loại nâng cao của (1 == 1)nút rơi vào mẫu if (<*> == <boolean_expr>).
Kaz

4

Sự khác biệt pratical là không có. 0được đánh giá false1được đánh giá true. Thực tế là bạn sử dụng biểu thức boolean ( 1 == 1) hoặc 1, để xác định true, sẽ không tạo ra bất kỳ sự khác biệt nào. Cả hai đều được đánh giá int.

Lưu ý rằng thư viện chuẩn C cung cấp một tiêu đề cụ thể để xác định booleans : stdbool.h.


tất nhiên là bạn chưa ... nhưng một số người có thể nghĩ khác đặc biệt là đối với các số âm đó là lý do :)
Pinkpanther

Gì? Bạn có nó ngược. trueđược đánh giá 1falseđược đánh giá 0. C không biết về các kiểu boolean bản địa, chúng chỉ là số nguyên.
djechlin

Trong C, các toán tử quan hệ và đẳng thức mang lại kết quả loại int, với giá trị 0hoặc 1. C có một kiểu boolean thực tế ( _Bool, với một macro boolđược định nghĩa trong <stdbool.h>, nhưng nó chỉ được thêm vào trong C99, không thay đổi ngữ nghĩa của các toán tử để sử dụng loại mới.
Keith Thompson

@djechlin: Tính đến tiêu chuẩn năm 1999, C không có kiểu boolean bản địa. Nó được gọi _Bool<stdbool.h>#define bool _Bool.
Keith Thompson

@KeithThndry, bạn nói đúng về việc 1 == 1được đánh giá là int. Đã chỉnh sửa.
Giày

3

Chúng tôi không biết giá trị chính xác mà TRUE bằng và trình biên dịch có thể có định nghĩa riêng. Vì vậy, những gì bạn có quyền riêng tư là sử dụng nội bộ của trình biên dịch để định nghĩa. Điều này không phải lúc nào cũng cần thiết nếu bạn có thói quen lập trình tốt nhưng có thể tránh được các vấn đề đối với một số kiểu mã hóa xấu, ví dụ:

if ((a> b) == TRUE)

Đây có thể là một thảm họa nếu bạn xác định thủ công TRUE là 1, trong khi giá trị bên trong của TRUE là một giá trị khác.


Trong C, >toán tử luôn mang lại 1 cho true, 0 cho false. Không có khả năng bất kỳ trình biên dịch C nào bị lỗi này. So sánh bình đẳng TRUEFALSElà phong cách kém; ở trên được viết rõ ràng hơn như if (a > b). Nhưng ý tưởng rằng các trình biên dịch C khác nhau có thể đối xử với sự thật và sai khác nhau chỉ là không chính xác.
Keith Thompson

2
  1. Danh sách mục

Thông thường trong Ngôn ngữ lập trình C, 1 được định nghĩa là đúng và 0 được định nghĩa là sai. Do đó tại sao bạn thấy sau đây khá thường xuyên:

#define TRUE 1 
#define FALSE 0

Tuy nhiên, bất kỳ số nào không bằng 0 cũng sẽ được đánh giá là đúng trong một tuyên bố có điều kiện. Do đó bằng cách sử dụng dưới đây:

#define TRUE (1==1)
#define FALSE (!TRUE)

Bạn chỉ có thể chỉ ra một cách rõ ràng rằng bạn đang cố gắng chơi nó an toàn bằng cách làm sai bằng với bất cứ điều gì không đúng.


4
Tôi sẽ không gọi nó là "chơi an toàn" - thay vào đó, bạn đang tạo cho mình một cảm giác an toàn sai lầm.
dodgethesteamler
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.