Reputation: 27
This seems really weird to me, and I've never seen any discussion of it in resources about C enums. The variable a
is of type enum Direction
, and it can be -1, but a<0
will be false. What am I missing about enums?
#include <stdio.h>
enum Direction {N,W,S,E};
int main()
{
enum Direction a = N;
printf("a is %d\n", a);
a--;
printf("now a is %d\n", a);
printf("but (a<0) is %d\n\n", (a<0));
int b = N;
printf("b is %d\n", b);
b--;
printf("now b is %d\n", b);
printf("and (b<0) is %d\n", (b<0));
return 0;
}
The output is
a is 0
now a is -1
but (a<0) is 0
b is 0
now b is -1
and (b<0) is 1
Upvotes: 2
Views: 695
Reputation: 310960
It depends on what integer type is selected by the compiler to be compatible with the enumeration type. The compiler can select an unsigned integer type.
In this statement
printf("now a is %d\n", a);
you are using the conversion specifier %d
designed for signed integer types though the enumeration can be compatible with an unsigned integer type and if you will use the conversion specifier %u
you will get another result.
If for example you will declare an enumeration constant as having a negative value then the compiler will select a signed integer type as compatible with the enumeration.
Here are two demonstrative programs that show the difference
#include <stdio.h>
int main( void )
{
enum Direction {N,W,S,E};
enum Direction a = N;
--a;
printf( "a < 0 is %d\n", a < 0 );
}
The output of this program is
a < 0 is 0
The compiler selected an unsigned integer type as the compatible integer type.
#include <stdio.h>
int main( void )
{
enum Direction {N = -1, W,S,E};
enum Direction a = W; // W is equal to 0
--a;
printf( "a < 0 is %d\n", a < 0 );
}
The output of this program is
a < 0 is 1
because the compiler selected a signed integer type as the compatible integer type.
Form the C Standard (6.7.2.2 Enumeration specifiers)
4 Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined,128) but shall be capable of representing the values of all the members of the enumeration. T
That is there is a difference between an enumeration type and an enumeration constant. Enumeration constants always have the type int
. But enumeration types are compatible with integer types (signed or unsigned) selected by the compiler.
Compare the two calls of printf
in this demonstration program.
#include <stdio.h>
int main( void )
{
enum Direction {N,W,S,E};
enum Direction a = N;
printf( "a - 1 < 0 is %d\n", a - 1 < 0 );
printf( "N - 1 < 0 is %d\n", N - 1 < 0 );
}
where the program output is
a - 1 < 0 is 0
N - 1 < 0 is 1
Upvotes: 2
Reputation: 834
The pitfall is your format specifier when printing a
. The format specifier %d
lets printf
interpret a
as a signed integer, no matter what data type it actually is. But actually, it is an unsigned integer, and a--
resulted in an overflow, i.e. a == 4294967295
. Hence a < 0
is false
. When interpreting this value as a signed integer, it is -1. Try using %u
as format specifier when printing a
and you will see the real value of a
.
Upvotes: 1
Reputation: 3172
The underlying type of an enum is an implementation-defined integer type that can represent all items of the enum. Here, it could be char
, int
, unsigned int
(possibly others).
Your implementation apparently chose to use unsigned int
; it's consistent with the comparison to 0 (can't be negative), and you only have it printed as -1
because in 2-complement, -1 has the same representation as "largest unsigned of the same width" (which is the value you have, because unsigned 0 minus 1 will wrap to the largest value).
It only prints as -1
because of the %d
format of printf, which converts it back to signed. But because it's unsigned, you should have used %u
.
When you convert it to a signed integer by creating an int var, you have an actual int, so it behaves correctly (printed as -1
by the %d
format of printf).
Upvotes: 2