开发者

How to determine whether a variable is unsigned or not in ANSI C

I am learning "Expert C Programming" by Peter Van Der Linden. In chapter A.6, the writter described how to determine whether a variable is unsigned or not in K&R C.The macro is below:

#define ISUNSIGNED(a) (a>=0 && ~a>=0)

The book is very old, it was first published in 1994! And I have not learned K&R C before. The question is that how to determine whether a variable is unsigned or not in ANSI C.

I have tried to solve the problem like this. Since "0" is int in ANSI C, and any other number except float, double and long double will be converted to int or unsigned int by Integer Upgrade when compare with 0. So I want to find an edge between unsigned and signed number. When I compare (the edge type)0 with a,开发者_运维知识库 the type of a will not be changed. The macro also the model is below:

#define ISUNSIGNED(a) (a>=(the edge type)0 && ~a>=(the edge type)0)

I can not find the edge type, is there anybody can help me solve the problem? I have changed "number" to "variable" for more accurate expression.


How this works

A signed variable has to store its sign in some bit. Usually this is the most significant one, but it could be any of them. An unsigned variable has no sign bits; thus, the lowest value it can hold is 0. This means that for an unsigned variable a, the expression a >= 0 will always be true.

So we have:

( a >= 0 && ~a >= 0 )

If a is unsigned, the first is true (it has to be), and the second is true (because whatever value ~a is, it's still an unsigned value, so it's still >= 0). If a is signed, that means that if the sign bit is set, a >= 0 is false (and the expression returns false, stating that this variable has a signed type). If the sign bit isn't set in a, then when ~a inverts all the bits in a, the sign bit (whichever one it is) has to be set. This means that it has to be a negative number, which means that ~a >= 0 returns false.

This does rely on the standard integer promotions to work like you'd expect them to.

How it doesn't work

unsigned char x = 1; // or whatever

printf("%s\n", ISUNSIGNED(x) ? "TRUE" : "FALSE"); // prints "FALSE"

As someone else pointed out, unsigned char gets promoted to an int since any value of ~a for an unsigned char a can easily fit in the range of an int. This is arguably a failing in the standard integer promotions (or a failing in the typing of integral literals).

There might be another implementation of ISUNSIGNED or ISSIGNED somewhere that can overcome this limitation. The P99 macro library has some mind-bending uses of macros, many relying on C99's variadic macros, but unfortunately the macro to check whether an expression is signed or not (#define SIGNED(expr) ((1 ? -1 : expr) < (1 ? 0 : expr))) succumbs to the same integer promotions. This might be the best you can do (though I suppose it's better than nothing in the cases where you'll want it).


You can't.

A signed long/int/short/char is negative if the MSB is set. An unsigned long/int/short/char is NOT negative if the MSB is set. In tabular form:

          MSB=0  MSB=1
unsigned  +      +
signed    +      -

The answer therefore is: signedness is an interpretation. The same number (sequence of bytes) can be interpreted as signed or unsigned; you can't decide if a number is signed or not by inspecting its value/contents. That's what static typing is (also) for.

note: in the comments it was mentioned that C probably doesn't mandate the MSB to be the "sign" for signed integer types. This is almost always true, though.

note2: the original formulation of the question asked about determining the signedness of a number, not of a variable (hence my answer about interpretation and static typing in C)


#define ISUNSIGNED(a) (a>=0 && ((a=~a)>=0 ? (a=~a, 1) : (a=~a, 0)))


#define ISUNSIGNED(type) (((type)-1) >= 0)

or

#define ISUNSIGNED(a) (((typeof(a))-1) >= 0) // This is a GNU extension


For anyone coming to this question but not restricted to C89 (technically C11 is also ANSI C because ANSI has ratified it):

#include <stdio.h>
#include <limits.h>

#define ISSIGNED(x) _Generic((x), \
    unsigned char: 0,       \
    unsigned short: 0,      \
    unsigned int: 0,        \
    unsigned long: 0,       \
    unsigned long long: 0,  \
    signed char: 1,         \
    signed short: 1,        \
    signed int: 1,          \
    signed long: 1,         \
    signed long long: 1,    \
    char: (CHAR_MIN < 0)    \
)

int main()
{
    char x;
    printf("%d\n", ISSIGNED(x));
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜