Tại sao toán tử bậc ba được sử dụng để xác định 1 và 0 trong macro?


79

Tôi đang sử dụng SDK cho một dự án nhúng. Trong mã nguồn này, tôi tìm thấy một số mã mà ít nhất tôi thấy đặc biệt. Ở nhiều nơi trong SDK có mã nguồn ở định dạng này:

#define ATCI_IS_LOWER( alpha_char )  ( ( (alpha_char >= ATCI_char_a) && (alpha_char <= ATCI_char_z) ) ? 1 : 0 )

#define ATCI_IS_UPPER( alpha_char )  ( ( (alpha_char >= ATCI_CHAR_A) && (alpha_char <= ATCI_CHAR_Z) ) ? 1 : 0 )

Việc sử dụng toán tử bậc ba ở đây có tạo ra sự khác biệt nào không?

Không

#define FOO (1 > 0)

giống như

#define BAR ( (1 > 0) ? 1 : 0)

?

Tôi đã thử đánh giá nó bằng cách sử dụng

printf("%d", FOO == BAR);

và nhận được kết quả 1, vì vậy có vẻ như chúng bằng nhau. Có lý do gì để viết mã như họ đã làm không?


8
Không, không có lý do. Bạn đúng rồi.
Nghệ thuật

29
Lạc đề một phần: Khi nào thì sự điên rồ của việc sử dụng bộ tiền xử lý dừng lại? Có nhiều khả năng đánh giá các chức năng liên quan ở đây. Chỉ là không cần thiết.
stefan

3
Đôi khi, thật tuyệt khi được tường minh. Toán tử bậc ba ở đây làm rõ ràng rằng mục đích của macro là trả về một boolean.
pipe,

5
Ít nhất, các macro nên sử dụng (alpha_char)thay thế alpha_char, chỉ để đảm bảo rằng nó không bị hỏng nếu ai đó cố gắng điều gì đó điên rồ như vậyATCI_IS_LOWER(true || -1) .
Justin Time - Phục hồi Monica

5
Hình như kiểu CI viết lâu rồi. Tôi muốn đến C từ Pascal, trong đó có một chuyên dụng booleanloại, vì vậy tôi lãng phí thời gian Vượt qua muôn vàn thay đổi khủng khiếp như if (n)để if (0 != n), có lẽ thêm một dàn diễn viên đáng ngờ "để đảm bảo". Tôi chắc chắn rằng tôi cũng đã gạch đầu dòng những bất đẳng thức như if (a < b) ...vậy. Chắc chắn nó trông giống như của Pascal if a < b then ..., nhưng tôi biết rằng C < không phải là một booleanmà là một int, và một intcó thể là bất cứ điều gì ! Sợ hãi dẫn đến mạ vàng, dát vàng dẫn đến hoang tưởng, hoang tưởng dẫn đến ... mã như thế.
Kevin J. Chase

Câu trả lời:


131

Bạn đã đúng, trong C nó là tính tế nhị. Cả hai điều kiện bậc ba cụ thể của bạn (1 > 0) đều thuộc loại int.

Nhưng nó sẽ quan trọng trong C ++, trong một số trường hợp góc gây tò mò (ví dụ như tham số cho các hàm được nạp chồng), vì biểu thức điều kiện bậc ba của bạn là kiểu int, trong khi đó (1 > 0)là kiểu bool.

Tôi đoán rằng tác giả đã đặt một số suy nghĩ vào điều này, với mục đích duy trì khả năng tương thích C ++.


2
Tôi nghĩ rằng các bool <-> intchuyển đổi được ngầm định trong C ++ bởi §4.7 / 4 từ tiêu chuẩn (chuyển đổi tích phân), vậy nó sẽ quan trọng như thế nào?
Motun

70
Hãy xem xét hai quá tải của một hàm foo, một hàm lấy một const bool&hàm kia lấy a const int&. Một trong số họ trả tiền cho bạn, người kia định dạng lại đĩa cứng của bạn. Bạn có thể muốn đảm bảo rằng bạn đang gọi đúng quá tải trong trường hợp đó.
Bathsheba

3
Sẽ không rõ ràng hơn nếu xử lý trường hợp này bằng cách truyền kết quả intthay vì bằng cách sử dụng dấu ba?
martinkunev

18
@Bathsheba Trong khi đó là một trường hợp hợp pháp, bất kỳ lập trình viên nào sử dụng quá tải tích hợp để thực hiện hành vi không nhất quán như vậy là hoàn toàn xấu.
JAB

7
@JAB: Bạn không cần phải xấu xa, bạn chỉ cần mắc phải lỗi (phổ biến) là viết một đoạn mã vô tình làm hai việc khác nhau (hoặc tệ hơn, gọi hành vi không xác định ) tùy thuộc vào loại tích phân và có thật không may khi làm như vậy ở một nơi có thể kích hoạt các đường dẫn mã hoàn toàn khác nhau.

28

Có những công cụ linting cho rằng kết quả của phép so sánh là boolean và không thể sử dụng trực tiếp trong số học.

Không phải để đặt tên hay chỉ bất kỳ ngón tay nào, nhưng PC-lint là một công cụ kẻ viền như vậy .

Tôi không nói rằng họ đúng, nhưng đó là một lời giải thích khả thi cho việc tại sao mã được viết như vậy.


10
Not to name names or point any fingers,nhưng bạn đã làm cả hai, lol.
StackOverflowed

19

Đôi khi bạn sẽ thấy điều này trong mã rất cũ , từ trước khi có tiêu chuẩn C để viết chính tả (x > y)đánh giá thành số 1 hoặc 0; một số CPU muốn đánh giá đó là −1 hoặc 0, và một số trình biên dịch rất cũ có thể đã làm theo, vì vậy một số lập trình viên cảm thấy họ cần thêm sự phòng thủ.

Đôi khi bạn cũng sẽ thấy điều này vì các biểu thức tương tự không nhất thiết phải đánh giá thành số 1 hoặc 0. Ví dụ: trong

#define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) ? 1 : 0)

&biểu thức bên trong đánh giá bằng 0 hoặc giá trị số F_DO_GRENFELZ, có thể không phải là 1, vì vậy các ? 1 : 0phục vụ để chuẩn hóa nó. Cá nhân tôi nghĩ sẽ rõ ràng hơn khi viết điều đó là

#define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) != 0)

nhưng những người hợp lý có thể không đồng ý. Nếu bạn có một loạt các biểu thức này liên tiếp, thử nghiệm các loại biểu thức khác nhau, ai đó có thể đã quyết định rằng sẽ dễ bảo trì hơn nếu đặt ? 1 : 0ở cuối tất cả chúng hơn là lo lắng về việc cái nào thực sự cần nó.


Nhìn chung, tôi thích sử dụng !!( expr )để chuẩn hóa một boolean, nhưng tôi sẽ thừa nhận rằng nó rất khó hiểu nếu bạn không quen thuộc với nó.
PJTraill

1
@PJTraill Mỗi khi bạn đặt dấu cách vào bên trong dấu ngoặc đơn, Chúa sẽ giết một con mèo con. Xin vui lòng. Hãy nghĩ về những chú mèo con.
zwol

Đó là lý do tốt nhất tôi đã nghe nói rằng không đặt khoảng trắng bên trong dấu ngoặc trong chương trình C.
PJTraill

15

Có một lỗi trong mã SDK, và có thể là một con chim mồi khó sửa chữa.

Là một macro, các đối số (alpha_char) có thể là bất kỳ biểu thức nào và phải được đặt trong ngoặc đơn vì các biểu thức như 'A' && 'c' sẽ không kiểm tra được.

#define IS_LOWER( x ) ( ( (x >= 'a') && (x <= 'z') ) ?  1 : 0 )
std::cout << IS_LOWER('A' && 'c');
**1**
std::cout << IS_LOWER('c' && 'A');
**0**

Đây là lý do tại sao người ta phải luôn đặt dấu ngoặc đơn đối số macro trong phần mở rộng.

Vì vậy, trong ví dụ của bạn (nhưng với các tham số), cả hai đều bị nghe lén.

#define FOO(x) (x > 0)
#define BAR(x) ((x > 0) ? 1 : 0)

Chính xác nhất chúng sẽ được thay thế bằng

#define BIM(x) ((x) > 0)

@CiaPan Có một điểm tuyệt vời trong nhận xét sau đó là việc sử dụng một tham số nhiều lần dẫn đến kết quả không xác định được. Ví dụ

#define IS_LOWER( x ) (((x) >= 'a') && ((x) <= 'z'))
char ch = 'y';
std::cout << IS_LOWER(ch++);
**1** 
**BUT ch is now '{'**

4
Lỗi khác là tham số được sử dụng hai lần, vì vậy một cuộc tranh cãi với tác dụng phụ sẽ dẫn đến kết quả không thể đoán trước: IS_LOWER(++ var)có thể tăng varmột hoặc hai lần, ngoài ra nó có thể không thông báo và nhận ra chữ thường 'z'nếu var'y'trước khi cuộc gọi vĩ mô. Đó là lý do tại sao nên tránh các macro như vậy, hoặc chỉ chuyển tiếp đối số tới một hàm.
CiaPan

5

Trong C nó không quan trọng. Biểu thức boolean trong C có kiểu intvà giá trị là 0hoặc 1, vì vậy

ConditionalExpr ? 1 : 0

không có hiệu lực.

Trong C ++, nó thực sự là một phép ép kiểu int, vì các biểu thức điều kiện trong C ++ có kiểu bool.

#include <stdio.h>
#include <stdbool.h>

#ifndef __cplusplus

#define print_type(X) _Generic(X, int: puts("int"), bool: puts("bool") );

#else
template<class T>
int print_type(T const& x);
template<> int print_type<>(int const& x) { return puts("int"); }
template<> int print_type<>(bool const& x) { return puts("bool"); }


#endif

int main()
{
    print_type(1);
    print_type(1 > 0);
    print_type(1 > 0 ? 1 : 0);

/*c++ output:
  int 
  int 
  int

  cc output:
  int
  bool
  int
*/

}

Cũng có thể không có hiệu ứng nào được dự định, và tác giả chỉ nghĩ rằng nó làm cho mã rõ ràng hơn.


BTW, tôi nghĩ C nên làm theo bộ và tạo biểu thức boolean _Bool, bây giờ C có _Bool_Generic. Nó không nên phá vỡ nhiều mã vì dù sao thì tất cả các loại nhỏ hơn đều tự động quảng cáo inttrong hầu hết các ngữ cảnh.
PSkocik,

5

Một lời giải thích đơn giản là một số người không hiểu rằng một điều kiện sẽ trả về cùng một giá trị trong C, hoặc họ nghĩ rằng viết sẽ sạch hơn ((a>b)?1:0).

Điều đó giải thích tại sao một số cũng sử dụng các cấu trúc tương tự trong các ngôn ngữ có boolean thích hợp, trong cú pháp C sẽ là như vậy (a>b)?true:false).

Điều này cũng giải thích tại sao bạn không nên thay đổi macro này một cách không cần thiết.


0

Có thể, là một phần mềm nhúng, sẽ cung cấp một số manh mối. Có thể có nhiều macro được viết bằng cách sử dụng kiểu này, để dễ dàng gợi ý rằng các dòng ACTI sử dụng logic trực tiếp hơn là logic ngược.

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.