这是在《C专家编程》一书上提到的一个问题。我在网上搜索了一下,找到了一些解法,总结如下。
首先是《C专家编程》上提到解法:
#define ISUNSIGNED(a) (a>=0 && ~a>=0) #define ISUNSIGNED(type) ((type)0-1 > 0) |
第二个从类型来判断,没有问题。
-----------------------------------------------------------------------------------------------------------------------------
1、判断一个数是否是无符号数
#define ISUNSIGNED(a) (a>=0 && ~a>=0)
解析:(1)如果一个数小于0,则它是有符号数;
(2)如果一个数大于0,则对它按位取反(包括符号位),若仍大于0,则为无符号数,否则为有符号数。
2、判断一个类型是否是无符号类型
#define ISUNSIGNED(type) ((type)0 - 1 >0)
解析:1、根据C语言的类型自动提升原则,如果type是有符号的,则与1计算的结果是有符号的,计算过程不发生类型提升,结果小于0;
2、如果type是无符号的,则发生类型提升,1先转换为无符号类型,计算结果同样为无符号类型,结果大于0。
----------------------------------------------------------------------------------------------------------------------------------------------------------------
而第一个只能用在K&R C里,在ANSI C里就不行了。当这个宏被用在int/unsigned int时,没有任何问题。但是当使用在char和short上就会出错。
分析: 此宏在int/unsigned int好使的原因是宏中的a的精度始终不会发生变化,而当a是char和short时,表达式~a>=0会先将a变为int然后再取反(因为常量0的类型为int),而int当然是有符号的,对它取反就有可能小于0。
要修正这个错误其实很简单,那就是要保证先进行取反操作再做精度提升,我们可以采用对a取反a=~a(这样就不会有类型提升),判断结束后再取一次反,恢复a的值。
这样得到新的代码:
#define ISUNSIGNED(a) (a >=0 && (a=~a,a >=0 ? (a=~a,1):(a=~a,0)))
另一种方法是:把变量的最高位置1,然后判断变量是否大于0。如果大于0,就是无符号数;否则就是有符号数。
#defined ISUNSIGNED(a) (a|=(0x1<<(sizeof(a)*8-1))>0?1:0)
还有人给出的方法是使用函数重载的方法:
void f(int val)
{
std::cout << "singned" << std::endl;
}
void f(unsigned int val)
{
std::cout << "unsigned" << std::endl;
}
在判断a是否有符号整数的地方调用函数f(a)就可以了吧
下面是对上面测试程序:
#include <iostream>
#define ISUNSIGNED(A) ((A|=(1<<(sizeof(A)*8-1)))>0?1:0)
#define ISUNSIGNED2(N) (N>=0&&~N>=0)
#define ISUNSIGNED3(N) (N>=0&&-N>=0&&N-1>=0)
#define ISUNSIGNED4(N) (N>=0&&(N=~N,N>=0?(N=~N,1):(N=~N,0)))
#define TEST(n) if(ISUNSIGNED4(n))\
cout<<#n" is unsigned"<<endl;\
else \
cout<<#n" is signed"<<endl;
using std::cout;
using std::endl;
void isUnsigned(){
cout<<"*********** test int ************"<<endl;
int i = 5;
unsigned int ui = 5;
TEST(i)
TEST(ui)
cout<<"*********** test long int ************"<<endl;
long li = 55;
unsigned long uli = 55;
TEST(li)
TEST(uli)
cout<<"*********** test short ************"<<endl;
short s = 3;
unsigned short us = 3;
TEST(s)
TEST(us)
cout<<"*********** test char ************"<<endl;
signed char c = 18;
unsigned char uc = 18;
TEST(c)
TEST(uc)
}
void f(int val){
cout<<"signed"<<endl;
}
void f(unsigned int val){
cout<<"unsigned"<<endl;
}
int main(){
isUnsigned();
return 0;
}