作为一项练习,我想编写一个宏来告诉我一个整数变量是否带符号。这是我目前的代码,如果我在gcc -fsigned-char或-funsigned-char上尝试对char变量进行操作,我会得到预期的结果。
#define ISVARSIGNED(V) (V = -1, (V < 0) ? 1 : 0)
这个是否是可移植的?有没有一种方法可以在不破坏变量值的情况下实现这一点?
作为一项练习,我想编写一个宏来告诉我一个整数变量是否带符号。这是我目前的代码,如果我在gcc -fsigned-char或-funsigned-char上尝试对char变量进行操作,我会得到预期的结果。
#define ISVARSIGNED(V) (V = -1, (V < 0) ? 1 : 0)
这个是否是可移植的?有没有一种方法可以在不破坏变量值的情况下实现这一点?
#define ISVARSIGNED(V) ((V)<0 || (-V)<0 || (V-1)<0)
第三个测试处理V == 0的情况,但不会改变V的值。
在我的编译器(gcc/cygwin)中,这适用于int和long,但不适用于char或short。
#define ISVARSIGNED(V) ((V)-1<0 || -(V)-1<0)
同时也能在两个测试中完成任务。
short
和 char
,您可以在使用变量之前将其转换为 char
。这应该能够处理大多数溢出问题。我认为... - Chris Lutz#define ISVARSIGNED(V) ((-(V) < 0) != ((V) < 0))
在不破坏变量值的情况下。但对于0值无效。
那么,这种情况怎么处理呢:
#define ISVARSIGNED(V) (((V)-(V)-1) < 0)
typeof
关键字来避免覆盖该值:#define ISVARSIGNED(V) ({ typeof (V) _V = -1; _V < 0 ? 1 : 0 })
这里创建了一个临时变量_V
,它的类型与V
相同。
至于可移植性,我不确定。它可以在补码机器上运行(也就是你的代码可能会在所有计算机上运行),我认为它也可以在反码和原码机器上运行。另外,如果您使用typeof
,您可能需要将-1
转换为typeof(V)
以使其更安全(即更少出现警告)。
这个简单的解决方案没有任何副作用,包括仅引用v一次(在宏中很重要)的好处。我们使用gcc扩展"typeof"来获取v的类型,然后将-1强制转换为该类型:
#define IS_SIGNED_TYPE(v) ((typeof(v))-1 <= 0)
使用“<=”而不是“<”可以避免某些情况下(启用时)编译器警告。
#define ISVARSIGNED(V) (~(V^V)<0)
这样就不需要为不同的 V 值设置特殊情况,因为 ∀ V ∈ ℤ,V^V = 0。
#define HIGH_BIT(n) ((n) & (1 << sizeof(n) * CHAR_BITS - 1))
#define IS_SIGNED(n) (HIGH_BIT(n) ? HIGH_BIT(n >> 1) != 0 : HIGH_BIT(~n >> 1) != 0
基本上,此宏使用条件表达式来确定数字的高位是否设置。如果没有设置,则通过按位取反该数字来设置它。我们不能进行算术否定,因为-0==0。然后,我们右移1位并测试是否发生符号扩展。
这假定2的补码算术,但通常是一种安全的假设。
char
类型。 :)
这就是为什么我写下它更适合在PC上这样做的原因。但是,我怀疑在我还在编程时可能会引入16位字符类型。(难道.NET没有16位的char
吗?) - sbi你到底为什么需要它成为一个宏?使用模板非常好:
template <typename T>
bool is_signed(T) {
static_assert(std::numeric_limits<T>::is_specialized, "Specialize std::numeric_limits<T>");
return std::numeric_limits<T>::is_signed;
}
这将适用于所有基本整数类型,开箱即用。但对于指针,在仅使用减法和比较的版本可能无法在编译时通过。
编辑:糟糕,问题要求使用C语言。不过,模板是一种好方法:P