Helpex - Trao đổi & giúp đỡ Đăng nhập
241

Ở đâu MINMAXđược định nghĩa trong C, nếu có?

Cách tốt nhất để thực hiện những điều này, một cách khái quát và gõ an toàn nhất có thể là gì? (Tiện ích mở rộng trình biên dịch / nội dung cho trình biên dịch chính được ưa thích.)

241 hữu ích 0 bình luận 642k xem chia sẻ
13 trả lời 13
326

Ở đâu MINMAXđược định nghĩa trong C, nếu có?

Họ không.

Cách tốt nhất để thực hiện những điều này là gì, nói chung và gõ an toàn nhất có thể (phần mở rộng / phần mềm biên dịch cho trình biên dịch chính được ưa thích).

Là chức năng. Tôi sẽ không sử dụng các macro như #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)), đặc biệt nếu bạn có kế hoạch triển khai mã của mình. Hoặc là viết riêng của bạn, sử dụng giống như tiêu chuẩn fmaxhoặc fmin, hoặc sửa chữa các vĩ mô sử dụng typeof của GCC (bạn nhận được typesafety thưởng quá):

 #define max(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a > _b ? _a : _b; })

Mọi người đều nói "ồ tôi biết về đánh giá kép, không vấn đề gì" và vài tháng sau, bạn sẽ gỡ lỗi cho những vấn đề khó hiểu nhất trong nhiều giờ liền.

Lưu ý việc sử dụng __typeof__thay vì typeof:

Nếu bạn đang viết một tệp tiêu đề phải hoạt động khi được bao gồm trong các chương trình ISO C, hãy viết __typeof__thay vì typeof.

326 hữu ích 5 bình luận chia sẻ
83

Nó cũng được cung cấp trong các phiên bản GNU libc (Linux) và FreeBSD của sys / param.h và có định nghĩa được cung cấp bởi dreamlax.


Trên Debian:

$ uname -sr
Linux 2.6.11

$ cat /etc/debian_version
5.0.2

$ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

$ head -n 2 /usr/include/sys/param.h | grep GNU
This file is part of the GNU C Library.

Trên FreeBSD:

$ uname -sr
FreeBSD 5.5-STABLE

$ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

Các kho lưu trữ nguồn ở đây:

83 hữu ích 5 bình luận chia sẻ
68

Có một std::minstd::maxtrong C ++, nhưng AFAIK, không có tương đương trong thư viện chuẩn C. Bạn có thể tự xác định chúng bằng các macro như

#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))

Nhưng điều này gây ra vấn đề nếu bạn viết một cái gì đó như MAX(++a, ++b).

68 hữu ích 5 bình luận chia sẻ
20

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;
}
20 hữu ích 0 bình luận chia sẻ
18

Tôi không nghĩ rằng chúng là các macro được tiêu chuẩn hóa. Có các chức năng được tiêu chuẩn hóa cho điểm nổi đã có, fmaxfmin(và fmaxfcho phao, và fmaxlcho đôi dài).

Bạn có thể triển khai chúng dưới dạng macro miễn là bạn nhận thức được các vấn đề về tác dụng phụ / đánh giá kép.

#define MAX(a,b) ((a) > (b) ? a : b)
#define MIN(a,b) ((a) < (b) ? a : b)

Trong hầu hết các trường hợp, bạn có thể để nó cho trình biên dịch để xác định những gì bạn đang cố gắng làm và tối ưu hóa nó tốt nhất có thể. Mặc dù điều này gây ra vấn đề khi được sử dụng như thế nào MAX(i++, j++), tôi nghi ngờ sẽ có rất nhiều nhu cầu trong việc kiểm tra tối đa các giá trị gia tăng trong một lần. Tăng trước, sau đó kiểm tra.

18 hữu ích 3 bình luận chia sẻ
16

Đây là một câu trả lời muộn, do một sự phát triển khá gần đây. Vì OP đã chấp nhận câu trả lời dựa trên phần mở rộng GCC (và clang) không di động typeof- hoặc __typeof__cho 'C' sạch '- có một giải pháp tốt hơn có sẵn từ gcc-4.9 .

#define max(x,y) ( \
    { __auto_type __x = (x); __auto_type __y = (y); \
      __x > __y ? __x : __y; })

Lợi ích rõ ràng của phần mở rộng này là mỗi đối số macro chỉ được mở rộng một lần, không giống như __typeof__giải pháp.

__auto_typelà một dạng hạn chế của C ++ 11's auto. Nó không thể (hoặc không nên?) Được sử dụng trong mã C ++, mặc dù không có lý do chính đáng nào để không sử dụng các khả năng suy luận loại ưu việt autokhi sử dụng C ++ 11.

Điều đó nói rằng, tôi cho rằng không có vấn đề gì khi sử dụng cú pháp này khi macro được bao gồm trong một extern "C" { ... }phạm vi; ví dụ: từ một tiêu đề C. AFAIK, tiện ích mở rộng này chưa tìm thấy thông tin.

16 hữu ích 0 bình luận chia sẻ
11

Tôi đã viết phiên bản này hoạt động cho MSVC, GCC, C và C ++.

#if defined(__cplusplus) && !defined(__GNUC__)
#   include <algorithm>
#   define MIN std::min
#   define MAX std::max
//#   define TMIN(T, a, b) std::min<T>(a, b)
//#   define TMAX(T, a, b) std::max<T>(a, b)
#else
#       define _CHOOSE2(binoper, lexpr, lvar, rexpr, rvar) \
                ({ \
                        decltype(lexpr) lvar = (lexpr); \
                        decltype(rexpr) rvar = (rexpr); \
                        lvar binoper rvar ? lvar : rvar; \
                })
#       define _CHOOSE_VAR2(prefix, unique) prefix##unique
#       define _CHOOSE_VAR(prefix, unique) _CHOOSE_VAR2(prefix, unique)
#       define _CHOOSE(binoper, lexpr, rexpr) \
                _CHOOSE2( \
                        binoper, \
                        lexpr, _CHOOSE_VAR(_left, __COUNTER__), \
                        rexpr, _CHOOSE_VAR(_right, __COUNTER__) \
                )
#       define MIN(a, b) _CHOOSE(<, a, b)
#       define MAX(a, b) _CHOOSE(>, a, b)
#endif
11 hữu ích 1 bình luận chia sẻ
8

Nếu bạn cần tối thiểu / tối đa để tránh một nhánh đắt tiền, bạn không nên sử dụng toán tử ternary, vì nó sẽ biên dịch thành một bước nhảy. Liên kết dưới đây mô tả một phương pháp hữu ích để thực hiện chức năng tối thiểu / tối đa mà không cần phân nhánh.

http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax

8 hữu ích 4 bình luận chia sẻ
4

Tôi biết anh chàng nói "C" ... Nhưng nếu bạn có cơ hội, hãy sử dụng mẫu C ++:

template<class T> T min(T a, T b) { return a < b ? a : b; }

Nhập an toàn và không có vấn đề với ++ được đề cập trong các bình luận khác.

4 hữu ích 3 bình luận chia sẻ
3

Thật đáng để chỉ ra Tôi nghĩ rằng nếu bạn xác định minmaxvới đại học như

#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

sau đó để có được kết quả tương tự cho trường hợp đặc biệt fmin(-0.0,0.0)fmax(-0.0,0.0)bạn cần trao đổi các đối số

fmax(a,b) = MAX(a,b)
fmin(a,b) = MIN(b,a)
3 hữu ích 2 bình luận chia sẻ
3

Có vẻ như Windef.h(a la #include <windows.h>) có maxmin(chữ thường) macro, cũng gặp phải khó khăn "đánh giá kép", nhưng chúng ở đó cho những người không muốn tự cuộn lại :)

3 hữu ích 1 bình luận chia sẻ
0

Tối đa của hai số nguyên ab(int)(0.5((a+b)+abs(a-b))). Điều này cũng có thể làm việc với (double)fabs(a-b)cho đôi (tương tự cho phao)

0 hữu ích 3 bình luận chia sẻ
0

Liên quan đến nhận xét của Brett Hale , clangbắt đầu hỗ trợ __auto_typevào khoảng năm 2016 (xem bản vá ).

0 hữu ích 0 bình luận chia sẻ
loading
Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ c max min c-preprocessor , hoặc hỏi câu hỏi của bạn.

Có thể bạn quan tâm

loading