MIN và MAX trong C?

Tránh các phần mở rộng trình biên dịch không chuẩn và triển khai nó dưới dạng macro hoàn toàn an toàn loại trong tiêu chuẩn thuần túy C (ISO 9899: 2011).

Dung dịch

#define

GENERIC_MAX

(

x

,

y

)

((

x

)

>

(

y

)

?

(

x

)

:

(

y

))

#define

ENSURE_int

(

i

)

_Generic

((

i

),

int

:

(

i

))

#define

ENSURE_float

(

f

)

_Generic

((

f

),

float

:

(

f

))

#define

MAX

(

type

,

x

,

y

)

\

(

type

)

GENERIC_MAX

(

ENSURE_

##type(x), ENSURE_##type(y))

Sử dụng

MAX

(

int

,

2

,

3

)

Giải trình

Macro MAX tạo một macro khác dựa trên typetham số. Macro điều khiển này, nếu được triển khai cho loại đã cho, được sử dụng để kiểm tra cả hai tham số có đúng loại không. Nếu typekhông được hỗ trợ, sẽ có lỗi trình biên dịch.

Nếu x hoặc y không đúng loại, sẽ có lỗi trình biên dịch trong các ENSURE_macro. Nhiều macro như vậy có thể được thêm vào nếu nhiều loại được hỗ trợ. Tôi đã giả định rằng chỉ các loại số học (số nguyên, số float, con trỏ, v.v.) sẽ được sử dụng và không sử dụng cấu trúc hoặc mảng, v.v.

Nếu tất cả các loại đều đúng, macro GENERIC_MAX sẽ được gọi. Cần thêm dấu ngoặc đơn xung quanh mỗi tham số macro, như biện pháp phòng ngừa tiêu chuẩn thông thường khi viết macro C.

Sau đó, có các vấn đề thường gặp với các chương trình khuyến mãi ngầm trong C. Nhà ?:điều hành cân bằng toán hạng thứ 2 và thứ 3 với nhau. Ví dụ, kết quả của GENERIC_MAX(my_char1, my_char2)sẽ là một int. Để ngăn macro thực hiện các quảng cáo loại nguy hiểm tiềm tàng như vậy, loại cuối cùng được sử dụng cho loại dự định đã được sử dụng.

Cơ sở lý luận

Chúng tôi muốn cả hai tham số cho macro là cùng một loại. Nếu một trong số chúng thuộc loại khác, macro không còn là loại an toàn nữa, bởi vì một toán tử thích ?:sẽ mang lại các khuyến mãi kiểu ngầm. Và bởi vì nó, chúng ta cũng luôn cần đưa kết quả cuối cùng trở lại loại dự định như đã giải thích ở trên.

Một macro chỉ với một tham số có thể đã được viết theo cách đơn giản hơn nhiều. Nhưng với 2 hoặc nhiều tham số, cần phải bao gồm một tham số loại phụ. Bởi vì điều này thật không may là không thể:

// this won't work

#define

MAX

(

x

,

y

)

\

_Generic

((

x

),

\

int

:

GENERIC_MAX

(

x

,

ENSURE_int

(

y

))

\

float

:

GENERIC_MAX

(

x

,

ENSURE_float

(

y

))

\

)

Vấn đề là nếu macro ở trên được gọi là MAX(1, 2)với hai int, nó vẫn sẽ cố gắng mở rộng macro tất cả các kịch bản có thể có của _Genericdanh sách liên kết. Vì vậy, ENSURE_floatmacro cũng sẽ được mở rộng, mặc dù nó không liên quan int. Và vì macro cố ý chỉ chứa floatloại, mã sẽ không được biên dịch.

Để giải quyết điều này, tôi đã tạo tên macro trong giai đoạn tiền xử lý thay vào đó, với toán tử ##, để không có macro nào vô tình được mở rộng.

Ví dụ

#include

<stdio.h>

#define

GENERIC_MAX

(

x

,

y

)

((

x

)

>

(

y

)

?

(

x

)

:

(

y

))

#define

ENSURE_int

(

i

)

_Generic

((

i

),

int

:

(

i

))

#define

ENSURE_float

(

f

)

_Generic

((

f

),

float

:

(

f

))

#define

MAX

(

type

,

x

,

y

)

\

(

type

)

GENERIC_MAX

(

ENSURE_

##type(x), ENSURE_##type(y))

int

main

(

void

)

{

int

ia

=

1

,

ib

=

2

;

float

fa

=

3.0f

,

fb

=

4.0f

;

double

da

=

5.0

,

db

=

6.0

;

printf

(

"%d\n"

,

MAX

(

int

,

ia

,

ib

));

// ok

printf

(

"%f\n"

,

MAX

(

float

,

fa

,

fb

));

// ok

//printf("%d\n", MAX(int, ia, fa)); compiler error, one of the types is wrong

//printf("%f\n", MAX(float, fa, ib)); compiler error, one of the types is wrong

//printf("%f\n", MAX(double, fa, fb)); compiler error, the specified type is wrong

//printf("%f\n", MAX(float, da, db)); compiler error, one of the types is wrong

//printf("%d\n", MAX(unsigned int, ia, ib)); // wont get away with this either

//printf("%d\n", MAX(int32_t, ia, ib)); // wont get away with this either

return

0

;

}