Link to home
Start Free TrialLog in
Avatar of naseeam
naseeamFlag for United States of America

asked on

Why typecasting to incorrect type doesn't give compiler warning or error?

Please consider following 'C' language program.  In function InitUnlock( ), msk is typecasted to port_lock_t.  msk value isn't contained in port_lock_t.  Therefore, this typecast should give warning or error?  To compile this program I typed:   gcc -Wall port_lock.c -o port_lock

#include <stdio.h>

typedef enum
{
    PORT_LOCK_P00  = 0x00000002,
    PORT_LOCK_P01  = 0x00000004,
    PORT_LOCK_P02  = 0x00000008,
    PORT_LOCK_P03  = 0x00000010,
    PORT_LOCK_P04  = 0x00000020,
    PORT_LOCK_P05  = 0x00000040,
    PORT_LOCK_P06  = 0x00000080,
    PORT_LOCK_P08  = 0x00000100,
    PORT_LOCK_P09  = 0x00000200,
    PORT_LOCK_P10  = 0x00000400,
    PORT_LOCK_P11  = 0x00000800,
    PORT_LOCK_P12  = 0x00001000,
    PORT_LOCK_P17  = 0x00002000,
    PORT_LOCK_P18  = 0x00004000,
    PORT_LOCK_P19  = 0x00008000,
    PORT_LOCK_P20  = 0x00010000,
    PORT_LOCK_P21  = 0x00020000,
    PORT_LOCK_P22  = 0x00040000,
    PORT_LOCK_P23  = 0x00080000,
    PORT_LOCK_P24  = 0x00100000,
    PORT_LOCK_AP0  = 0x00200000,
    PORT_LOCK_AP1  = 0x00400000,
    PORT_LOCK_AP2  = 0x00800000,
    PORT_LOCK_AP3  = 0x01000000,
    PORT_LOCK_AP4  = 0x02000000,
    PORT_LOCK_AP5  = 0x04000000,
    PORT_LOCK_LVDS = 0x08000000
} port_lock_t;

const port_lock_t PortL_Array[] = {
    PORT_LOCK_P00,
    PORT_LOCK_P01,
    PORT_LOCK_P02,
    PORT_LOCK_P03,
    PORT_LOCK_P04,
    PORT_LOCK_P05,
    PORT_LOCK_P06,
    PORT_LOCK_P08,
    PORT_LOCK_P09,
    PORT_LOCK_P10,
    PORT_LOCK_P11,
    PORT_LOCK_P12,
    PORT_LOCK_P17,
    PORT_LOCK_P18,
    PORT_LOCK_P19,
    PORT_LOCK_P20,
    PORT_LOCK_P21,
    PORT_LOCK_P22,
    PORT_LOCK_P23,
    PORT_LOCK_P24,
    PORT_LOCK_AP0,
    PORT_LOCK_AP1,
    PORT_LOCK_AP2,
    PORT_LOCK_AP3,
    PORT_LOCK_AP4,
    PORT_LOCK_AP5,
    PORT_LOCK_LVDS
};

const unsigned int CfgCnt = sizeof(PortL_Array) / sizeof(PortL_Array[0]);

void PORT_Unlock(port_lock_t port_unlock)
{
    printf ("The value of port_unlock is %x \n", port_unlock);
}

void InitUnlock(const port_lock_t port[], unsigned char cnt)
{
    unsigned long int msk;
    unsigned char n;
    
    msk = (unsigned long int)0x0;

    for (n = 0; n < cnt; n++)
    {
	msk |= port[n];
    }

    PORT_Unlock((port_lock_t)msk);
}

int main (void)
{
    InitUnlock(PortL_Array, CfgCnt);

    return 0;
}

Open in new window

Avatar of phoffric
phoffric

In C, an enum is just a set of integers that are grouped together to improve the model. Typecasting an integer to a C enum is simply casting one integer type to another. Without the C enum, you could use #define, or use plain old int's. If you want to have checks that an integer value is contained within an enum type, then in C, you will have to write your own check function.
https://www.tutorialspoint.com/enum-in-c

Maybe another question you could ask is why the following code compiles and works with no typecasting:
https://ideone.com/hYuCNK
#include <stdio.h>

typedef enum
{
    PORT_LOCK_P00  = 0x00000002,
    PORT_LOCK_P01  = 0x00000004,
    PORT_LOCK_P02  = 0x00000008,
    PORT_LOCK_P03  = 0x00000010,
    PORT_LOCK_P04  = 0x00000020,
    PORT_LOCK_P05  = 0x00000040,
    PORT_LOCK_P06  = 0x00000080,
    PORT_LOCK_P08  = 0x00000100,
    PORT_LOCK_P09  = 0x00000200,
    PORT_LOCK_P10  = 0x00000400,
    PORT_LOCK_P11  = 0x00000800,
    PORT_LOCK_P12  = 0x00001000,
    PORT_LOCK_P17  = 0x00002000,
    PORT_LOCK_P18  = 0x00004000,
    PORT_LOCK_P19  = 0x00008000,
    PORT_LOCK_P20  = 0x00010000,
    PORT_LOCK_P21  = 0x00020000,
    PORT_LOCK_P22  = 0x00040000,
    PORT_LOCK_P23  = 0x00080000,
    PORT_LOCK_P24  = 0x00100000,
    PORT_LOCK_AP0  = 0x00200000,
    PORT_LOCK_AP1  = 0x00400000,
    PORT_LOCK_AP2  = 0x00800000,
    PORT_LOCK_AP3  = 0x01000000,
    PORT_LOCK_AP4  = 0x02000000,
    PORT_LOCK_AP5  = 0x04000000,
    PORT_LOCK_LVDS = 0x08000000
} port_lock_t;

const port_lock_t PortL_Array[] = {
    PORT_LOCK_P00,
    PORT_LOCK_P01,
    PORT_LOCK_P02,
    PORT_LOCK_P03,
    PORT_LOCK_P04,
    PORT_LOCK_P05,
    PORT_LOCK_P06,
    PORT_LOCK_P08,
    PORT_LOCK_P09,
    PORT_LOCK_P10,
    PORT_LOCK_P11,
    PORT_LOCK_P12,
    PORT_LOCK_P17,
    PORT_LOCK_P18,
    PORT_LOCK_P19,
    PORT_LOCK_P20,
    PORT_LOCK_P21,
    PORT_LOCK_P22,
    PORT_LOCK_P23,
    PORT_LOCK_P24,
    PORT_LOCK_AP0,
    PORT_LOCK_AP1,
    PORT_LOCK_AP2,
    PORT_LOCK_AP3,
    PORT_LOCK_AP4,
    PORT_LOCK_AP5,
    PORT_LOCK_LVDS
};

const unsigned int CfgCnt = sizeof(PortL_Array) / sizeof(PortL_Array[0]);

void PORT_Unlock(port_lock_t port_unlock)
{
    printf ("The value of port_unlock is hex integer %x \n", port_unlock);
}

void InitUnlock(const port_lock_t port[], unsigned char cnt)
{
    unsigned long int msk;
    unsigned char n;
    
    msk = (unsigned long int)0x0;

    for (n = 0; n < cnt; n++)
    {
	msk |= port[n];
    }

    PORT_Unlock(msk);
}

int main (void)
{
    InitUnlock(PortL_Array, CfgCnt);

    return 0;
}

Open in new window


The answer is that you are passing in an integer type, and a C enum is considered to be an integer type.
@naseeam

PortL_Array representing an arrray } [27] (from 0 to 26) of enum (you can see at gdb output)
Since we are using | OR operator using msk(unsigned long int) with enum value, the result is unsigned long int.
We can use OR operator of enum to integers.
printf( "%d\n", PortL_Array[0]); => will have the value 2
printf( "%x\n", PortL_Array[26]); => value being 8000000 (due to %x)
You can know the type of PortL_Array[0] using warning message:
$ /usr/bin/gcc -g -Wall ./port_lock.c
./port_lock.c: In function ‘main’:
./port_lock.c:82: warning: format ‘%lu’ expects type ‘long unsigned int’, but argument 2 has type ‘unsigned int’
$ awk '{ if ( 82 == NR ) print $0 }' port_lock.c
        printf( "%lu\n", PortL_Array[0]);

Open in new window

Hence PortL_Array[0] type being unsigned int WHEN passing that as a parameter to printf.


murugesandins@123.456.89.132 /home/murugesandins [ 0 ]
$ /usr/bin/gdb -q ./a.out
Reading symbols from /home/murugesandins/a.out...done.
(gdb) break main
Breakpoint 1 at 0x40054b: file ./port_lock.c, line 80.
(gdb) run
Starting program: /home/murugesandins/a.out
[Thread debugging using libthread_db enabled]

Breakpoint 1, main () at ./port_lock.c:80
warning: Source file is more recent than executable.
80              InitUnlock(PortL_Array, CfgCnt);
(gdb) step
InitUnlock (port=0x400680, cnt=27 '\033') at ./port_lock.c:69
69              unsigned long int msk = 0L;
(gdb) next
70              unsigned char n = 0L;
(gdb)
72              for (n = 0; n < cnt; n++)
(gdb)
74                      msk |= port[n];
(gdb) ptype PortL_Array
type = enum {PORT_LOCK_P00 = 2, PORT_LOCK_P01 = 4, PORT_LOCK_P02 = 8, PORT_LOCK_P03 = 16, PORT_LOCK_P04 = 32, PORT_LOCK_P05 = 64,
    PORT_LOCK_P06 = 128, PORT_LOCK_P08 = 256, PORT_LOCK_P09 = 512, PORT_LOCK_P10 = 1024, PORT_LOCK_P11 = 2048, PORT_LOCK_P12 = 4096,
    PORT_LOCK_P17 = 8192, PORT_LOCK_P18 = 16384, PORT_LOCK_P19 = 32768, PORT_LOCK_P20 = 65536, PORT_LOCK_P21 = 131072,
    PORT_LOCK_P22 = 262144, PORT_LOCK_P23 = 524288, PORT_LOCK_P24 = 1048576, PORT_LOCK_AP0 = 2097152, PORT_LOCK_AP1 = 4194304,
    PORT_LOCK_AP2 = 8388608, PORT_LOCK_AP3 = 16777216, PORT_LOCK_AP4 = 33554432, PORT_LOCK_AP5 = 67108864,
    PORT_LOCK_LVDS = 134217728} [27]
(gdb) ptype PortL_Array[0]
type = enum {PORT_LOCK_P00 = 2, PORT_LOCK_P01 = 4, PORT_LOCK_P02 = 8, PORT_LOCK_P03 = 16, PORT_LOCK_P04 = 32, PORT_LOCK_P05 = 64,
    PORT_LOCK_P06 = 128, PORT_LOCK_P08 = 256, PORT_LOCK_P09 = 512, PORT_LOCK_P10 = 1024, PORT_LOCK_P11 = 2048, PORT_LOCK_P12 = 4096,
    PORT_LOCK_P17 = 8192, PORT_LOCK_P18 = 16384, PORT_LOCK_P19 = 32768, PORT_LOCK_P20 = 65536, PORT_LOCK_P21 = 131072,
    PORT_LOCK_P22 = 262144, PORT_LOCK_P23 = 524288, PORT_LOCK_P24 = 1048576, PORT_LOCK_AP0 = 2097152, PORT_LOCK_AP1 = 4194304,
    PORT_LOCK_AP2 = 8388608, PORT_LOCK_AP3 = 16777216, PORT_LOCK_AP4 = 33554432, PORT_LOCK_AP5 = 67108864,
    PORT_LOCK_LVDS = 134217728}
(gdb) print port[n]
$1 = PORT_LOCK_P00
(gdb) ptype msk
type = long unsigned int
(gdb) ptype msk |= port[n]
type = long unsigned int
(gdb) print (long unsigned int)port[n]
$2 = 2
(gdb) quit
A debugging session is active.

        Inferior 1 [process 13019] will be killed.

Quit anyway? (y or n) yes

Open in new window


We can display the value of OR operator using enum

Few more information including updated code and comment using C program
murugesandins@123.456.89.132 /home/murugesandins [ 0 ]
$ cat ./port_lock.c
#include <string.h>
#include <stdio.h>
void IntToBinary(int val)
{
        // I am compiling at OS where int size being 4 bytes.
        int i = 0;
        char BinaryVal[33] = { '\0'}; // Hence using 32 bits
        memset( BinaryVal, '0', sizeof(BinaryVal)); // Intialize all bits to zero
        BinaryVal[32] = '\0'; // one byte being NULL character at the end of the string.
        for( i = 0; val > 0; ++i)
        {
                BinaryVal[31 - i] = (val % 2)+48;
                val /= 2;
        }
        printf( "%s", BinaryVal);
}
typedef enum
{
        PORT_LOCK_P00  = 0x00000002,
        PORT_LOCK_P01  = 0x00000004,
        PORT_LOCK_P02  = 0x00000008,
        PORT_LOCK_P03  = 0x00000010,
        PORT_LOCK_P04  = 0x00000020,
        PORT_LOCK_P05  = 0x00000040,
        PORT_LOCK_P06  = 0x00000080,
        PORT_LOCK_P08  = 0x00000100,
        PORT_LOCK_P09  = 0x00000200,
        PORT_LOCK_P10  = 0x00000400,
        PORT_LOCK_P11  = 0x00000800,
        PORT_LOCK_P12  = 0x00001000,
        PORT_LOCK_P17  = 0x00002000,
        PORT_LOCK_P18  = 0x00004000,
        PORT_LOCK_P19  = 0x00008000,
        PORT_LOCK_P20  = 0x00010000,
        PORT_LOCK_P21  = 0x00020000,
        PORT_LOCK_P22  = 0x00040000,
        PORT_LOCK_P23  = 0x00080000,
        PORT_LOCK_P24  = 0x00100000,
        PORT_LOCK_AP0  = 0x00200000,
        PORT_LOCK_AP1  = 0x00400000,
        PORT_LOCK_AP2  = 0x00800000,
        PORT_LOCK_AP3  = 0x01000000,
        PORT_LOCK_AP4  = 0x02000000,
        PORT_LOCK_AP5  = 0x04000000,
        PORT_LOCK_LVDS = 0x08000000
} port_lock_t;
const port_lock_t PortL_Array[] = {
        PORT_LOCK_P00,
        PORT_LOCK_P01,
        PORT_LOCK_P02,
        PORT_LOCK_P03,
        PORT_LOCK_P04,
        PORT_LOCK_P05,
        PORT_LOCK_P06,
        PORT_LOCK_P08,
        PORT_LOCK_P09,
        PORT_LOCK_P10,
        PORT_LOCK_P11,
        PORT_LOCK_P12,
        PORT_LOCK_P17,
        PORT_LOCK_P18,
        PORT_LOCK_P19,
        PORT_LOCK_P20,
        PORT_LOCK_P21,
        PORT_LOCK_P22,
        PORT_LOCK_P23,
        PORT_LOCK_P24,
        PORT_LOCK_AP0,
        PORT_LOCK_AP1,
        PORT_LOCK_AP2,
        PORT_LOCK_AP3,
        PORT_LOCK_AP4,
        PORT_LOCK_AP5,
        PORT_LOCK_LVDS
};
const unsigned int CfgCnt = sizeof(PortL_Array) / sizeof(PortL_Array[0]);
void PORT_Unlock(port_lock_t port_unlock)
{
        // printf ("The value of port_unlock is %x ungined int %lu Binary: ", port_unlock, port_unlock);
        // ./port_lock.c:75: warning: format ‘%lu’ expects type ‘long unsigned int’, but argument 3 has type ‘unsigned int’
        printf ("The value of port_unlock is %x ungined int %u Binary: ", port_unlock, port_unlock);
        // OR
        // printf ("The value of port_unlock is %x int %d Binary: ", port_unlock, port_unlock);
        IntToBinary( port_unlock);
        printf( "\n");
}
void InitUnlock(const port_lock_t port[], unsigned char cnt)
{
        // Always initialize when defininig
        unsigned long int msk = 0L;
        unsigned char n = 0L;
        // msk = (unsigned long int)0x0;
        for (n = 0; n < cnt; n++)
        {
                printf( "%09lu [ ", msk);
                IntToBinary(msk);
                printf( " ] | %09d [ ", port[n]);
                IntToBinary(port[n]);
                msk |= port[n];
                printf( " ] = %09lu\n", msk);
        }
        PORT_Unlock((port_lock_t)msk);
}
int main (void)
{
        InitUnlock(PortL_Array, CfgCnt);
        return 0;
}
murugesandins@123.456.89.132 /home/murugesandins [ 0 ]
$ /usr/bin/gcc -g -Wall ./port_lock.c -o ./a.out
murugesandins@123.456.89.132 /home/murugesandins [ 0 ]
$  ./a.out
000000000 [ 00000000000000000000000000000000 ] | 000000002 [ 00000000000000000000000000000010 ] = 000000002
000000002 [ 00000000000000000000000000000010 ] | 000000004 [ 00000000000000000000000000000100 ] = 000000006
000000006 [ 00000000000000000000000000000110 ] | 000000008 [ 00000000000000000000000000001000 ] = 000000014
000000014 [ 00000000000000000000000000001110 ] | 000000016 [ 00000000000000000000000000010000 ] = 000000030
000000030 [ 00000000000000000000000000011110 ] | 000000032 [ 00000000000000000000000000100000 ] = 000000062
000000062 [ 00000000000000000000000000111110 ] | 000000064 [ 00000000000000000000000001000000 ] = 000000126
000000126 [ 00000000000000000000000001111110 ] | 000000128 [ 00000000000000000000000010000000 ] = 000000254
000000254 [ 00000000000000000000000011111110 ] | 000000256 [ 00000000000000000000000100000000 ] = 000000510
000000510 [ 00000000000000000000000111111110 ] | 000000512 [ 00000000000000000000001000000000 ] = 000001022
000001022 [ 00000000000000000000001111111110 ] | 000001024 [ 00000000000000000000010000000000 ] = 000002046
000002046 [ 00000000000000000000011111111110 ] | 000002048 [ 00000000000000000000100000000000 ] = 000004094
000004094 [ 00000000000000000000111111111110 ] | 000004096 [ 00000000000000000001000000000000 ] = 000008190
000008190 [ 00000000000000000001111111111110 ] | 000008192 [ 00000000000000000010000000000000 ] = 000016382
000016382 [ 00000000000000000011111111111110 ] | 000016384 [ 00000000000000000100000000000000 ] = 000032766
000032766 [ 00000000000000000111111111111110 ] | 000032768 [ 00000000000000001000000000000000 ] = 000065534
000065534 [ 00000000000000001111111111111110 ] | 000065536 [ 00000000000000010000000000000000 ] = 000131070
000131070 [ 00000000000000011111111111111110 ] | 000131072 [ 00000000000000100000000000000000 ] = 000262142
000262142 [ 00000000000000111111111111111110 ] | 000262144 [ 00000000000001000000000000000000 ] = 000524286
000524286 [ 00000000000001111111111111111110 ] | 000524288 [ 00000000000010000000000000000000 ] = 001048574
001048574 [ 00000000000011111111111111111110 ] | 001048576 [ 00000000000100000000000000000000 ] = 002097150
002097150 [ 00000000000111111111111111111110 ] | 002097152 [ 00000000001000000000000000000000 ] = 004194302
004194302 [ 00000000001111111111111111111110 ] | 004194304 [ 00000000010000000000000000000000 ] = 008388606
008388606 [ 00000000011111111111111111111110 ] | 008388608 [ 00000000100000000000000000000000 ] = 016777214
016777214 [ 00000000111111111111111111111110 ] | 016777216 [ 00000001000000000000000000000000 ] = 033554430
033554430 [ 00000001111111111111111111111110 ] | 033554432 [ 00000010000000000000000000000000 ] = 067108862
067108862 [ 00000011111111111111111111111110 ] | 067108864 [ 00000100000000000000000000000000 ] = 134217726
134217726 [ 00000111111111111111111111111110 ] | 134217728 [ 00001000000000000000000000000000 ] = 268435454
The value of port_unlock is ffffffe ungined int 268435454 Binary: 00001111111111111111111111111110
murugesandins@123.456.89.132 /home/murugesandins [ 0 ]
$

Open in new window

@murugesandins,
Your two posts appear to be off topic. For example, there was nothing in the question about. OR operator precedence. I will ask a monitor to review. By keeping focused to the question, Google search hits will be more meaningful.
ASKER CERTIFIED SOLUTION
Avatar of sarabande
sarabande
Flag of Luxembourg image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
@Sara,
>> the cast in the code shown is bad programming because msk actually is not part of the valid enum constants defined.
Thanks for adding this point. I assumed that the work was in progress, or another person's code, and hence the question about why the compiler didn't protect the programmer from violating proper modeling techniques. But C doesn't do a very good job of protecting the programmer. I think I read that the purpose was to improve on assembly language code allowing a more sensible way to model the problem.

"C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off"
- Bjarne Stroustrup

>> the argument of PORT_Unlock should have been an unsigned int and no cast would have been needed.
Yes, by rights, if that is the intent of the model.
void PORT_Unlock(port_lock_t port_unlock)
{
    printf ("The value of port_unlock is %x \n", port_unlock);
}

Open in new window

But from this limited snippet, it is hard to decipher the overall context of the model. When designing/programming, we must always be sure we understand the model. For all we know from this small program, maybe PORT_Unlock has to accept just one port to unlock at a time due to hardware timing restrictions, or simply the hardware register's api. Maybe the InitUnlock function should include in its for loop the PORT_Unlock function and not try to use a msk variable. Hard to know without reading the hardware api specification.
But from this limited snippet, it is hard to decipher the overall context of the model.

yes and no. the name PORT_Unlock together with the names of the enum constants, tells pretty well which purpose was intended. the printf statement shows that the function is not yet finished. and the call of teh function shows that the author has (mis)used the function to get a debug output of the msk variable which has all valid "port bits" set.

note, if they would have added a enum constant like

typedef enum 
{
     PORT_LOCK_P00  = 0x00000002,
     ....
     PORT_LOCK_ALL = PORT_LOCK_P00 | PORT_LOCK_P01 | .... PORT_LOCK_LVDS

Open in new window


the code

port_lock_t msk = PORT_LOCK_ALL;
PORT_Unlock(msk);

Open in new window


would be valid and no typecast needed. actually, nearly all non-trivial  C based headers and many C++ headers were using unsigned integers of size 8, 16, 32, or 64 bits as "bit containers" what could be a really smart way to handle several boolean cases with only one integer variable. c++ provides a bitset class for this but the c way to handle bit containers often is much better readable and less error-prone if done correctly.

Sara
>> yes and no.
Agreed. So I will not try to second guess the author who can decide whether further clarification is required, or whether the question is answered.

>> c++ provides a bitset class
Since the domain is C, I try not to bring in the benefits of C++ for this author since I believe he is working in a C embedded system. But if he wants to create a new C++ question on this topic, we can address that ad well.
Avatar of naseeam

ASKER

Outstanding solutions.  Best ever!  I can't imagine better solution.  Multiple solutions.  Answered the exact question with lots of insight and ideas.  Brilliant!  I wish I could learn 'C' the right way from you experts.

Consideration of the opposite question and solution was really good.  It provided good insight.  It was very helpful.