답변:
C에서 어디에
MIN
있고MAX
정의되어 있습니까?
그렇지 않습니다.
일반적으로 유형을 최대한 안전하게 구현하는 가장 좋은 방법은 무엇입니까?
기능으로. #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
특히 코드를 배포하려는 경우 와 같은 매크로를 사용하지 않습니다 . 직접 작성하거나 standard 또는와 같은 것을 사용 fmax
하거나 GCC 문 표현식 에서 GCC의 typeof (typesafety 보너스를 얻음)를 fmin
사용하여 매크로를 수정하십시오 .
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
모두들 "오, 나는 이중 평가에 대해 알고있다. 아무 문제가 없다"고 말하고 몇 달 동안 길을 가다 보면 가장 어려운 문제들을 몇 시간 동안 디버깅하게 될 것이다.
__typeof__
대신에 typeof
다음을 사용하십시오 .
ISO C 프로그램에 포함될 때 작동해야하는 헤더 파일을 작성하는 경우
__typeof__
대신 대신 작성하십시오typeof
.
decltype
키워드 를 엉망으로 만드는 것입니다. 그러나 Visual Studio는 매크로에서 복합 명령문을 수행 할 수 없습니다 (그리고 decltype
어쨌든 C ++입니다), 즉 GCC의 ({ ... })
구문이므로 어쨌든 불가능하다는 것을 확신합니다. 이 문제와 관련하여 다른 컴파일러를 보지 못했습니다. 죄송합니다. Luther : S
MAX(someUpperBound, someRandomFunction())
임의의 값을 상한으로 제한 하는 경우를 보았습니다 . 그것은 끔찍한 아이디어 였지만 사용하지 않았기 때문에 MAX
이중 평가 문제가 있었기 때문에 처음 평가 된 것과 다른 임의의 숫자로 끝났습니다.
MIN(x++, y++)
전처리기를 호출 하면 다음 코드가 생성됩니다 (((x++) < (y++)) ? (x++) : (y++))
. 그래서, x
및 y
두 배 증가합니다.
또한 GNU libc (Linux) 및 FreeBSD 버전의 sys / param.h에서 제공되며 dreamlax에서 제공하는 정의를 가지고 있습니다.
데비안에서 :
$ 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.
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))
소스 리포지토리는 다음과 같습니다.
openSUSE/Linux 3.1.0-1.2-desktop
/ gcc version 4.6.2 (SUSE Linux)
에도 작동합니다 . :) 휴대용이 아닙니다.
C ++ 에는 a std::min
와 std::max
C가 있지만 C 표준 라이브러리에는 AFAIK가 없습니다. 다음과 같은 매크로로 직접 정의 할 수 있습니다
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
그러나 이와 같은 것을 쓰면 문제가 발생합니다 MAX(++a, ++b)
.
#define MIN(A, B) ((A < B) ? A : B)
유연한 방법이 아니라고 말한 퀴즈를 찾았습니다 . 왜 ???
#define MULT(x, y) x * y
. 그런 다음로 MULT(a + b, a + b)
확장되어 우선 순위에 따라 a + b * a + b
구문 분석됩니다 a + (b * a) + b
. 프로그래머가 의도 한 것이 아닙니다.
비표준 컴파일러 확장을 피하고 순수 표준 C (ISO 9899 : 2011)에서 완전 형식 안전 매크로로 구현하십시오.
해결책
#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))
용법
MAX(int, 2, 3)
설명
매크로 MAX는 type
매개 변수를 기반으로 다른 매크로를 만듭니다 . 지정된 유형에 대해 구현 된 경우이 제어 매크로는 두 매개 변수가 모두 올바른 유형인지 확인하는 데 사용됩니다. 이 type
지원되지 않으면 컴파일러 오류가 발생합니다.
x 또는 y가 올바른 유형이 아닌 경우 ENSURE_
매크로에 컴파일러 오류가 발생 합니다. 더 많은 유형이 지원되는 경우 더 많은 매크로를 추가 할 수 있습니다. 나는 산술 유형 (정수, 부동 소수점, 포인터 등) 만 사용되며 구조체 또는 배열 등은 사용하지 않는다고 가정했습니다.
모든 유형이 올 바르면 GENERIC_MAX 매크로가 호출됩니다. C 매크로를 작성할 때 일반적인 표준주의 사항으로 각 매크로 매개 변수 주위에 추가 괄호가 필요합니다.
그런 다음 C에서 암시 적 유형 승격에 일반적인 문제점이 있습니다. ?:
연산자는 두 번째와 세 번째 피연산자의 균형을 맞 춥니 다. 예를 들어 결과는 GENERIC_MAX(my_char1, my_char2)
입니다 int
. 매크로가 잠재적으로 위험한 유형 승격을 수행하지 못하게하기 위해 최종 유형을 의도 된 유형으로 캐스트했습니다.
이론적 해석
매크로에 대한 두 매개 변수가 모두 같은 유형이 되길 원합니다. 그중 하나가 다른 유형 인 경우 매크로는 더 이상 유형 안전하지 않습니다. 같은 연산자 ?:
는 암시 적 유형 승격을 생성 하기 때문 입니다. 그렇게하기 때문에, 우리는 또한 항상 최종 결과를 위에서 설명한대로 의도 된 유형으로 다시 변환해야합니다.
매개 변수가 하나만있는 매크로는 훨씬 간단한 방식으로 작성되었을 수 있습니다. 그러나 2 개 이상의 매개 변수를 사용하면 추가 유형 매개 변수를 포함해야합니다. 이와 같은 것은 불행히도 불가능하기 때문에 :
// this won't work
#define MAX(x, y) \
_Generic((x), \
int: GENERIC_MAX(x, ENSURE_int(y)) \
float: GENERIC_MAX(x, ENSURE_float(y)) \
)
문제는 위의 매크로가 MAX(1, 2)
two 로 호출 된 경우 int
에도 _Generic
연결 목록 의 가능한 모든 시나리오를 매크로 확장하려고 시도한다는 것 입니다. 따라서 ENSURE_float
관련이 없어도 매크로가 확장됩니다 int
. 그리고 그 매크로는 의도적으로 float
타입 만 포함하기 때문에 코드는 컴파일되지 않습니다.
이 문제를 해결하기 위해 ## 연산자를 사용하여 사전 프로세서 단계에서 매크로 이름을 만들어 실수로 매크로가 확장되지 않도록했습니다.
예
#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;
}
GENERIC_MAX
매크로는 나쁜 생각이지만, GENERIC_MAX(var++, 7)
왜 :-) 오늘날 (특히 엄청나게 최적화 / 인라이닝 컴파일러를 사용하여) 매크로를 간단한 형식으로 만 강등 해야하는지 알아 내려고 노력해야 합니다. 함수형 함수는 함수보다 낫고 값 그룹은 열거 형보다 낫습니다.
나는 그들이 표준화 된 매크로라고 생각하지 않습니다. 표준화 이미 부동 소수점을위한 기능이 있습니다 fmax
과 fmin
(와 fmaxf
수레를 위해, 그리고 fmaxl
긴 복식은).
부작용 / 이중 평가 문제를 알고있는 한 매크로로 매크로를 구현할 수 있습니다.
#define MAX(a,b) ((a) > (b) ? a : b)
#define MIN(a,b) ((a) < (b) ? a : b)
대부분의 경우 컴파일러에 맡겨서 수행하려는 작업을 결정하고 최대한 최적화 할 수 있습니다. 이처럼 사용할 때 문제가 발생하지만 MAX(i++, j++)
한 번에 최대 증가 값을 확인해야 할 필요성이 의심 스럽습니다. 먼저 증분 한 다음 확인하십시오.
이것은 최근의 개발로 인해 늦은 답변입니다. OP가 이식 불가능한 GCC (및 클랜) 확장 typeof
또는 __typeof__
'깨끗한'ISO C 에 의존하는 대답을 받아들 였으므로 gcc-4.9부터 더 나은 솔루션을 사용할 수 있습니다 .
#define max(x,y) ( \
{ __auto_type __x = (x); __auto_type __y = (y); \
__x > __y ? __x : __y; })
이 확장의 명백한 이점은 __typeof__
솔루션 과 달리 각 매크로 인수가 한 번만 확장된다는 것입니다.
__auto_type
C ++ 11의 제한된 형식입니다 auto
. C ++ 코드에서는 auto
C ++ 11 을 사용할 때 우수한 형식 유추 기능을 사용하지 않을 이유가 없지만 C ++ 코드에서 사용할 수 없습니다 .
즉, 매크로가 범위에 포함될 때이 구문을 사용하는 데 문제가 없다고 가정 합니다 extern "C" { ... }
. 예를 들어 C 헤더에서. AFAIK,이 확장 프로그램은 정보 clang을 찾지 못했습니다
c-preprocessor
태그 가 있지만 동의합니다 . gcc의 __always_inline__
속성 과 같은 것을 사용하지 않는 한, 해당 키워드로도 함수가 인라인되도록 보장되지 않습니다 .
MSVC, GCC, C 및 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
값 비싼 분기를 피하기 위해 min / max가 필요한 경우 삼항 연산자를 사용하면 안됩니다. 점프로 컴파일됩니다. 아래 링크는 분기없이 최소 / 최대 기능을 구현하는 유용한 방법을 설명합니다.
http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
@ 데이비드 Titarenco 여기에 못을 박았다 하지만 좋은 보이게하고, 모두를 보여주기 위해 깨끗한 적어도 약간을 나를 수 있도록 min()
하고 max()
복사하고 여기에 쉽게에서 붙여 만들기 위해 함께. :)
2020 년 4 월 25 일 업데이트 : 또한 C와 C ++를 배우거나 서로 전환하는 사람들을위한 귀중한 비교로서 C ++ 템플릿을 사용하여이 작업을 수행하는 방법을 보여주기 위해 섹션 3을 추가했습니다. 나는이 답을 반복해서 다시 올 수있는 정식 참조로 만들기 위해 철저하고 사실적이며 정확하기 위해 최선을 다했습니다.
이 기술은 일반적으로 올바르게 사용하는 방법, "사실상"일을하는 방법을 잘 알고 올바르게 사용하는 경우 사용하는 것이 좋지만 버그가있는 경우 ( 이중 평가 부작용 ) 생각 하는 사람들이 잘 사용 합니다. 변수 할당 을 포함한 표현식을 전달 하여 비교하십시오.
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
이 기술은 위의 "이중 평가"부작용 및 버그를 피하므로,이를 수행하기위한 우수하고, 안전하며 "더 현대적인" GCC C 방식으로 간주됩니다 . clang은 의도적으로 gcc와 호환되므로 gcc 및 clang 컴파일러에서 모두 작동 할 것으로 예상됩니다 (이 답변의 맨 아래에있는 clang 참고 참조).
그러나 명령문 표현식이 명백하게 인라인되어 있으므로 자체 로컬 변수 범위가 없으므로 " 변수 섀도 잉 "효과를 여전히주의하십시오!
#define max(a,b) \
({ \
__typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; \
})
#define min(a,b) \
({ \
__typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; \
})
gcc 문 표현식 에서 코드 블록 의 마지막 표현식 은 함수에서 반환 된 것처럼 표현식에서 "반환 된"것입니다. GCC 문서 는 다음과 같이 말합니다.
복합 명령문의 마지막은 세미콜론 뒤에 오는 표현식이어야합니다. 이 하위 표현식의 값은 전체 구문의 값으로 사용됩니다. (중괄호 내에서 마지막으로 다른 종류의 명령문을 사용하면 구문의 유형이 void이므로 사실상 값이 없습니다.)
C ++ 참고 : C ++을 사용하는 경우 템플릿이 대신이 유형의 구문에 권장되지만 템플릿을 개인적으로 싫어하고 어쨌든 임베디드 C ++에서도 C 스타일을 자주 사용하고 선호하므로 C ++에서 위의 구문 중 하나를 사용합니다.
이 섹션은 2020 년 4 월 25 일에 추가되었습니다.
지난 몇 달 동안 많은 C ++을 해왔으며 가능한 경우 C ++ 커뮤니티에서 매크로보다 템플릿을 선호해야한다는 압박이 강합니다. 결과적으로 템플릿 사용에 익숙해졌으며 여기에 C ++ 템플릿 버전을 추가하여 완성도를 높이고보다 정식적이고 철저한 답변을 얻고 자합니다.
여기에 기본 무슨 기능 템플릿 의 버전 max()
및 min()
C ++에서처럼 보일 수 있습니다 :
template <typename T>
T max(T a, T b)
{
return a > b ? a : b;
}
template <typename T>
T min(T a, T b)
{
return a < b ? a : b;
}
C ++ 템플릿에 대한 추가 정보는 Wikipedia : Template (C ++)을 참조하십시오 .
그러나 두 max()
와 min()
이미의 C ++ 표준 라이브러리의 일부이다 <algorithm>
헤더 ( #include <algorithm>
). C ++ 표준 라이브러리에서는 위의 것과 약간 다르게 정의됩니다. 기본에 대한 프로토 타입 std::max<>()
과는 std::min<>()
, 예를 들어, C ++ 14, 단지 위의 cplusplus.com 링크에서 자신의 프로토 타입을보고 있습니다 :
template <class T>
constexpr const T& max(const T& a, const T& b);
template <class T>
constexpr const T& min(const T& a, const T& b);
키워드가 있습니다 typename
별칭이 class
(자신의 사용은 당신이 말하는 여부 동일하므로 <typename T>
또는 <class T>
이 나중에 C ++ 템플릿의 발명 이후에 인정 된 이후 템플릿 유형은 일반 유형이 될 수 있음을, () int
, float
등) 대신 만의 클래스 타입.
여기에서 입력 유형과 반환 유형이 모두 const T&
"유형에 대한 상수 참조"임을 알 수 T
있습니다. 이는 입력 매개 변수와 리턴 값이 value가 아닌 참조 로 전달됨을 의미합니다 . 이것은 포인터를 지나가는 것과 같으며 클래스 객체와 같은 큰 유형에 더 효율적입니다. constexpr
기능의 일부 수정이 함수 자체 및 함수가 나타내는 수 있어야 컴파일 시간 (최소 제공된 경우에 평가되고 constexpr
그것을 컴파일 시간에서 평가 될 수 없다면 디폴트가있는 다시, 입력 파라미터)하지만 다른 일반 기능과 마찬가지로 런타임 평가.
(A)의 컴파일 시간 양태 constexpr
C ++ 함수는 친절한 컴파일시에 평가가 가능한 경우 그는에서 C 매크로처럼 만드는 constexpr
기능이 동일한 것처럼, 컴파일 시간에 수행 될 MIN()
또는 MAX()
매크로 대체 가능성있을 C 또는 C ++에서도 컴파일 타임에 완전히 평가됩니다. 이 C ++ 템플릿 정보에 대한 추가 참조는 아래를 참조하십시오.
위키 백과의 Clang 메모 :
[Clang]은 대부분의 컴파일 플래그 및 비공식 언어 확장을 지원하는 GCC (GNU Compiler Collection)의 드롭 인 대체 역할을하도록 설계되었습니다.
내가 생각에서 그 가치를 가리키는 것 당신이 정의하는 경우 그 min
와 max
같은 차 등으로
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
다음의 특별한 경우에 대해 동일한 결과를 얻을 수 fmin(-0.0,0.0)
및 fmax(-0.0,0.0)
당신은 인수를 교체 할 필요가
fmax(a,b) = MAX(a,b)
fmin(a,b) = MIN(b,a)
fmin(3.0,NaN)==fmin(NaN,3.0)==fmax(3.0,NaN)==fmax(NaN,3.0)==3.0
같은 외모는 Windef.h
(라 #include <windows.h>
)가 max
와 min
(소문자) 매크로는 또한 "이중 평가"어려움으로 고통, 그러나 그렇지 않은 사람들을 위해 거기 있음을 다시 롤 자신 :) 원하는
나는 그 남자가 "C"라고 말한 것을 알고있다. 그러나 만약 당신이 기회가 있다면, C ++ 템플릿을 사용하라 :
template<class T> T min(T a, T b) { return a < b ? a : b; }
다른 주석에서 언급 한 ++에는 문제가 없으며 안전합니다.
최대 2 개의 정수 a
이며 b
입니다 (int)(0.5((a+b)+abs(a-b)))
. 이것은 또한 작동 할 수 (double)
및 fabs(a-b)
복식 (수레 유사)
가장 간단한 방법은 .h
파일 에서 전역 함수로 정의하고 프로그램이 많은 파일로 모듈 식인 경우 원할 때마다 호출하는 것입니다. 그렇지 않은 경우 double MIN(a,b){return (a<b?a:b)}
가장 간단한 방법입니다.
warning: expression with side-effects multiply evaluated by macro