 |
 |
View previous topic :: View next topic |
Author |
Message |
SamMeredith
Joined: 13 Jul 2018 Posts: 25
|
Unexpected behaviour when casting to enum |
Posted: Thu Feb 06, 2025 12:22 pm |
|
|
Compiler verson: 5.118
Device: DSPIC33EP512MC806
Consider the following example code:
Code: |
#include <33EP512MC806.h>
// #device CCS2
#case
#use delay( crystal=12Mhz, clock=120Mhz, AUX:clock=48Mhz )
#pin_select U3RX = PIN_F1
#pin_select U3TX = PIN_F2
#use rs232(UART3, baud = 115200, parity = N, bits = 8, errors, TIMEOUT = 1, stream = DEBUGSTREAM)
#word U3STA = getenv("SFR:U3STA")
#bit U3TRMT = U3STA.8
#define DEBUGPRINTF( x, ... ) { \
fprintf( DEBUGSTREAM, x, __VA_ARGS__ ); \
while ( !U3TRMT ) {}; \
}
typedef enum
{
OFF,
ON
} STATE;
typedef enum
{
TRISTATE_OFF,
TRISTATE_ON,
UNKNOWN
} TRISTATE;
void main()
{
delay_ms(100);
int1 tmp = 1;
STATE assign_enum = ON; // 01
STATE assign_literal = 1; // 01
STATE assign_variable = tmp; // 01
STATE cast_enum = (STATE)ON; // 00 ??
STATE cast_literal = (STATE)1; // 00 ??
STATE cast_variable = (STATE)tmp; // 01
DEBUGPRINTF(" assign_enum=%02X\r\n assign_literal=%02X\r\n assign_variable=%02X\r\n cast_enum=%02X\r\n cast_literal=%02X\r\n cast_variable=%02X\r\n",
assign_enum, assign_literal, assign_variable, cast_enum, cast_literal, cast_variable);
int tmp2 = 2;
TRISTATE tristate_assign_enum = UNKNOWN; // 02
TRISTATE tristate_assign_literal = 2; // 02
TRISTATE tristate_assign_variable = tmp2; // 02
TRISTATE tristate_cast_enum = (TRISTATE)UNKNOWN; // 02
TRISTATE tristate_cast_literal = (TRISTATE)2; // 02
TRISTATE tristate_cast_variable = (TRISTATE)tmp2; // 02
DEBUGPRINTF(" assign_enum=%02X\r\n assign_literal=%02X\r\n assign_variable=%02X\r\n cast_enum=%02X\r\n cast_literal=%02X\r\n cast_variable=%02X\r\n",
tristate_assign_enum, tristate_assign_literal, tristate_assign_variable, tristate_cast_enum, tristate_cast_literal, tristate_cast_variable);
tmp2 = 259;
tristate_assign_literal = 259; // 03
tristate_assign_variable = tmp2; // 03
tristate_cast_literal = (TRISTATE)259; // 03
tristate_cast_variable = (TRISTATE)tmp2; // 03
DEBUGPRINTF(" assign_literal=%02X\r\n assign_variable=%02X\r\n cast_literal=%02X\r\n cast_variable=%02X\r\n",
tristate_assign_literal, tristate_assign_variable, tristate_cast_literal, tristate_cast_variable);
while(TRUE) {}
}
|
The TRISTATE enum with 3 states behaves as I would expect. TRISTATE variables are represented as a single byte and casting behaves normally. I believe overflowing an enum is undefined behaviour in ANSII C but the CCS behaviour of simply wrapping makes sense.
The STATE enum however does something strange when explicitly casting. I would expect both cast_enum and cast_literal to be 1, but they are not.
Taking a look at the .LST file we can see what happens
Quote: |
.................... int1 tmp = 1;
....................
.................... STATE assign_enum = ON; // 01
.................... STATE assign_literal = 1; // 01
.................... STATE assign_variable = tmp; // 01
.................... STATE cast_enum = (STATE)ON; // 00 ??
.................... STATE cast_literal = (STATE)1; // 00 ??
.................... STATE cast_variable = (STATE)tmp; // 01
0038E: BSET.B 1002.0
00390: BSET.B 1002.1
00392: BSET.B 1002.2
00394: BCLR.B 1002.3
00396: BTSS.B 1002.0
00398: BRA 39C
0039A: BSET.B 1002.3
0039C: BCLR.B 1002.4
0039E: BCLR.B 1002.5
003A0: BCLR.B 1002.6
003A2: BTSS.B 1002.0
003A4: BRA 3A8
003A6: BSET.B 1002.6
|
The assignments make sense but the casts seem strange to me. The compiler just does a direct BCLR.B
I can't come up with an explanation for cast_enum and cast_literal evaluating to 0.
I found a reference to CCS compilation modes, and indeed #DEVICE=CCS2 behaves as I would expect (they evaluate to 1), but even with that knowledge the behaviour in the default mode doesn't make sense to me.
Does this seem like a bug, or is there a logical explanation for the behaviour?
EDIT: Swapped the order of enums to make more sense |
|
 |
Ttelmah
Joined: 11 Mar 2010 Posts: 19730
|
|
Posted: Sat Feb 08, 2025 2:57 am |
|
|
Not really.
Most C's, will actually give a warning if you cast like this, that you are
'mixing enum types'.
Your STATE type is being optimised to an int1, which sometimes has slightly
'odd' rules about what can and cannot be done, and since it is not actually
an int, the C appears to be making an error on the explicit cast. It gets
it right if you use the implicit cast though.
Normally all enum's are ints, this is an oddity because this one is not.
I have to say I dislike your use of capitals here. In C is it a 'semi standard' to
reserve ALL CAPITALS for macro defines, so these are easy to see. The norm
is also to use _t for declared types, so:
Code: |
typedef enum
{
TRISTATE_OFF,
TRISTATE_ON,
UNKNOWN
} tristate_t;
|
Is the standard way to do a declaration like this, which makes it much
less likely to make mistakes in the future or if the code is being passed
to another person.
Suggest you just use the implicit cast to do this, but report it to CCS
as a 'noted oddity', so they can fix it in the future. |
|
 |
SamMeredith
Joined: 13 Jul 2018 Posts: 25
|
|
Posted: Fri Feb 14, 2025 6:38 am |
|
|
Thanks Ttelmah, I'll report it to CCS.
I was trying to find a proper reference in the C spec but couldn't find one. As far as I could see enums in standard C will never be smaller than a char anyway.
I completely agree with you on capitalisation and _t for declared types.
I do exactly that on all my real code (VSCode highlights _t nicely).
Not sure why I didn't on the example. I'll blame it on being late one evening. |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|