我需要找到没有副作用的任意C表达式的最大值和最小值。以下宏可以在我的机器上运行。他们会在所有平台上工作吗?如果没有,他们可以修改工作吗?我的意图是随后使用这些来实现SAFE_MUL(a,b)
之类的宏来代替a*b
。 SAFE_MUL
将包括检查乘法溢出。
编辑:按史蒂夫的建议投射类型。
#include <stdio.h>
#include <limits.h>
#define IS_SIGNED(exp) (((exp)*0-1) < 0)
#define TYPE_MAX_UNSIGNED(exp) ((exp)*0-1)
#define TYPE_MAX_SIGNED(exp) ( \
sizeof (exp) == sizeof (int) \
? \
INT_MAX \
: \
( \
sizeof (exp) == sizeof (long) \
? \
LONG_MAX \
: \
LLONG_MAX \
) \
)
#define TYPE_MAX(exp) ((unsigned long long)( \
IS_SIGNED (exp) \
? \
TYPE_MAX_SIGNED (exp) \
: \
TYPE_MAX_UNSIGNED (exp) \
))
#define TYPE_MIN_SIGNED(exp) ( \
sizeof (exp) == sizeof (int) \
? \
INT_MIN \
: \
( \
sizeof (exp) == sizeof (long) \
? \
LONG_MIN \
: \
LLONG_MIN \
) \
)
#define TYPE_MIN(exp) ((long long)( \
IS_SIGNED (exp) \
? \
TYPE_MIN_SIGNED (exp) \
: \
(exp)*0 \
))
int
main (void) {
printf ("TYPE_MAX (1 + 1) = %lld\n", TYPE_MAX (1 + 1));
printf ("TYPE_MAX (1 + 1L) = %lld\n", TYPE_MAX (1 + 1L));
printf ("TYPE_MAX (1 + 1LL) = %lld\n", TYPE_MAX (1 + 1LL));
printf ("TYPE_MAX (1 + 1U) = %llu\n", TYPE_MAX (1 + 1U));
printf ("TYPE_MAX (1 + 1UL) = %llu\n", TYPE_MAX (1 + 1UL));
printf ("TYPE_MAX (1 + 1ULL) = %llu\n", TYPE_MAX (1 + 1ULL));
printf ("TYPE_MIN (1 + 1) = %lld\n", TYPE_MIN (1 + 1));
printf ("TYPE_MIN (1 + 1L) = %lld\n", TYPE_MIN (1 + 1L));
printf ("TYPE_MIN (1 + 1LL) = %lld\n", TYPE_MIN (1 + 1LL));
printf ("TYPE_MIN (1 + 1U) = %llu\n", TYPE_MIN (1 + 1U));
printf ("TYPE_MIN (1 + 1UL) = %llu\n", TYPE_MIN (1 + 1UL));
printf ("TYPE_MIN (1 + 1ULL) = %llu\n", TYPE_MIN (1 + 1ULL));
return 0;
}
答案 0 :(得分:3)
IS_SIGNED
宏并不能说明小于int
的无符号类型。 IS_SIGNED((unsigned char)1)
在任何正常实施中均属实,因为(unsigned char)1*0
的类型为int
,而不是unsigned char
。您的最终SAFE
宏仍应说明是否发生溢出,因为相同的整数提升适用于所有算术。但是他们会告诉你是否在乘法中出现溢出,不必然是否在用户将结果转换回其中一个操作数的原始类型时发生。
考虑到这一点,你可能已经知道了,因为你的宏不会尝试建议CHAR_MIN
等等。但是将来发现这个问题的其他人可能没有意识到这种限制。
没有一种类型可以保证能够保存TYPE_MIN
和TYPE_MAX
可以评估的所有值。但是,您可以TYPE_MAX
始终评估为unsigned long long
(并且值始终适合该类型),TYPE_MIN
和signed long long
也一样。这将允许您使用正确的printf格式,而无需使用您对表达式是否已签名的了解。目前TYPE_MAX(1)
为long long
,而TYPE_MAX(1ULL)
为unsigned long long
。
从技术上讲,允许int
和long
具有相同的大小但不同的范围,因为long
的填充位数少于int
。我怀疑任何重要的实现都是这样做的。
答案 1 :(得分:1)
只是一个想法:如果你使用gcc,你可以使用它的typeof
扩展名:
#define IS_SIGNED(exp) ((typeof(exp))-1 < 0)
#define TYPE_MAX_UNSIGNED(exp) ((typeof(exp))-1)
#define TYPE_MAX_SIGNED(exp) ... // i cannot improve your code here
编辑:也可能会检查浮点类型:
#define CHECK_INT(exp) ((typeof(exp))1 / 2 == 0)
#define CHECK_INT(exp) (((exp) * 0 + 1) / 2 == 0) // if cannot use typeof
#define MY_CONST_1(exp) (1/CHECK_INT(exp))
// Now replace any 1 in code by MY_CONST_1(exp) to cause error for floating-point