看起来我已经能够想出一个相对容易使用的宏,SIGNED_VAR(VAR),通过修改、比较和恢复变量的值来测试给定的整数变量是否有符号(所有这些都只是必要的对于小于int) 的类型,同时避免未定义的行为,特别是与有符号溢出和序列点相关的类型。或者看起来是这样。至少,gcc(用-Wall 调用)不会抱怨我在&& 和|| 运算符之间做疯狂的事情,尽管它不喜欢在三元?: 运算符周围做同样的事情。
这个宏的好处是它应该与 C89 和 C99 编译器一起使用(1LL 可以替换为 1L 和 long long 可以替换为 long (和 "%ll" 与 @987654333 @,当然)如果您的 C89 编译器没有来自 C99 的扩展 long long 类型)并且它还正确支持小于 int 的类型(char 和 short)。
一旦我们知道变量是否有符号,构造最小值和最大值就很简单了,许多人已经展示了如何做到这一点。宏VAR_MAX() 和VAR_MIN() 构造这些值并将它们作为最长的C99 整数类型long long 返回。在将无符号值转换为有符号时,我选择返回有符号类型以避免潜在的溢出/UB 问题。由于返回类型long long不能直接将unsigned long long(ULLONG_MAX)的最大值表示为有符号值,如果需要返回该值,则返回-1,在转换为unsigned long long之后将产生ULLONG_MAX。您需要在这里小心一点。
丑陋的东西来了。希望,我没有错过任何错误。
哦,当然,有符号类型(例如 min=-128、max=+127)支持 2 的补码的整个不对称范围。
编辑:我忘了提到SIGNED_VAR() 期望变量被初始化。否则,读取它可能会导致未定义的行为。
// file: IntVarMinMax.c
// compile: gcc -Wall -std=c99 -O2 IntVarMinMax.c -o IntVarMinMax.exe
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int SignTestTestVal;
unsigned char SignTestOriginalXchar;
unsigned short SignTestOriginalXshort;
signed char SignTestRestoreOriginalXchar(void)
{
if (SignTestOriginalXchar < SCHAR_MAX + 1u)
return (signed char)SignTestOriginalXchar;
return (signed char)(SignTestOriginalXchar - SCHAR_MAX - 1) - SCHAR_MAX - 1;
}
short SignTestRestoreOriginalXshort(void)
{
if (SignTestOriginalXshort < SHRT_MAX + 1u)
return (short)SignTestOriginalXshort;
return (short)(SignTestOriginalXshort - SHRT_MAX - 1) - SHRT_MAX - 1;
}
#define IFELSE(E1,E2,E3) (((E1) && (E2)) || (!(E1) && (E3)))
#define SEQ(E1,E2) (((E1) && (E2)) || (E2))
#define SIGNED_VAR(VAR) \
( \
IFELSE \
( \
sizeof(VAR) >= sizeof(int), \
((VAR) - (VAR) - 1 < 0), \
IFELSE \
( \
sizeof(VAR) == sizeof(short), \
SEQ(SignTestOriginalXshort = (VAR), \
SEQ(SignTestTestVal = (VAR) = -1, \
SEQ((VAR) = SignTestRestoreOriginalXshort(), \
SignTestTestVal < 0))), \
IFELSE \
( \
sizeof(VAR) == sizeof(char), \
SEQ(SignTestOriginalXchar = (VAR), \
SEQ(SignTestTestVal = (VAR) = -1, \
SEQ((VAR) = SignTestRestoreOriginalXchar(), \
SignTestTestVal < 0))), \
(fprintf(stderr, "unsupported type!"), exit(-1), 0) \
) \
) \
) \
)
#define VAR_MAX(SIGNED,VAR) \
( \
SIGNED ? \
((1ll << (sizeof(VAR) * CHAR_BIT - 2)) - 1 + \
(1ll << (sizeof(VAR) * CHAR_BIT - 2))) : \
( \
(sizeof(VAR) < sizeof(long long)) ? \
((1ll << (sizeof(VAR) * CHAR_BIT - 1)) - 1 + \
(1ll << (sizeof(VAR) * CHAR_BIT - 1))) : \
( \
(sizeof(VAR) == sizeof(long long)) ? \
-1ll : \
(fprintf(stderr, "unsupported type!"), exit(-1), 0) \
) \
) \
)
#define VAR_MIN(SIGNED,VAR) \
( \
SIGNED ? \
(-((1ll << (sizeof(VAR) * CHAR_BIT - 2)) - 1 + \
(1ll << (sizeof(VAR) * CHAR_BIT - 2))) - 1) : \
0 \
)
int main(void)
{
signed char sc = 1; char c = 2; unsigned char uc = 3;
short ss = 4; unsigned short us = 5;
int si = 6; unsigned int ui = 7;
long sl = 8; unsigned long ul = 9;
long long sll = 10; unsigned long long ull = 11;
#define PRINT_VARS() \
printf("sc=%hhd, c=%hhu, uc=%hhu, " \
"ss=%hd, us=%hu, si=%d, ui=%u, " \
"sl=%ld, ul=%lu, sll=%lld, ull=%llu\n", \
sc, c, uc, \
ss, us, si, ui, \
sl, ul, sll, ull)
#define TEST_VAR(VAR) \
{ \
int varIsSigned = SIGNED_VAR(VAR); \
if (varIsSigned) \
printf("%lld <= " #VAR " <= %lld\n", \
VAR_MIN(varIsSigned,VAR), \
VAR_MAX(varIsSigned,VAR)); \
else \
printf("%lld <= " #VAR " <= %llu\n", \
VAR_MIN(varIsSigned,VAR), \
(unsigned long long)VAR_MAX(varIsSigned,VAR)); \
}
PRINT_VARS();
TEST_VAR(sc);
TEST_VAR(c);
TEST_VAR(uc);
TEST_VAR(ss);
TEST_VAR(us);
TEST_VAR(si);
TEST_VAR(ui);
TEST_VAR(sl);
TEST_VAR(ul);
TEST_VAR(sll);
TEST_VAR(ull);
PRINT_VARS();
return 0;
}
输出(ideone):
sc=1, c=2, uc=3, ss=4, us=5, si=6, ui=7, sl=8, ul=9, sll=10, ull=11
-128 <= sc <= 127
-128 <= c <= 127
0 <= uc <= 255
-32768 <= ss <= 32767
0 <= us <= 65535
-2147483648 <= si <= 2147483647
0 <= ui <= 4294967295
-2147483648 <= sl <= 2147483647
0 <= ul <= 4294967295
-9223372036854775808 <= sll <= 9223372036854775807
0 <= ull <= 18446744073709551615
sc=1, c=2, uc=3, ss=4, us=5, si=6, ui=7, sl=8, ul=9, sll=10, ull=11