I ran across some code that concerned me and want to understand why it works. Basically it went like this...
/* myFile.c */
int number0 = 0;
int number1 = 1;
int number2 = 2;
int number3 = 3;
typedef struct
{
void *addr;
int count;
} myTable_T;
const myTable_T configTable[4] = {
{ &number0, 10 },
{ &number1, 20 },
( &number2, 30 },
{ &number3, 40 }
};
void myRoutine()
{
const myTable_T *pTbl;
int j;
for (j = 0; j < 4; j++)
{
pTbl = configTable[j].addr;
if ((int) pTbl->addr != 0)
{
// do something
}
}
The lines that concern me are where pTbl is assigned, and then used in the condition. First, pTbl is a pointer to a struct myTable_T, but the right hand assignment is a void pointer. Then the next line does treat it as a struct still, as if struct member "addr" is actually another struct (it's not). How does that even work? It does function correctly. Also, even if the assignment to pTbl does actually return the struct pointer, doesn't the int cast actually cast away the void pointer? Because we need two dereferences, one for the pointer to the struct, one for the pointer to member addr.
I think at the least that pTbl can't access the second member in the struct. Later in the code, when the second member (count) is accessed, it's done directly from configTable, rather than through pointer pTbl. I think they tried to access it with pTbl but couldn't, so just went at it directly!
I'd expect to see those two lines something like:
pTbl = &configTable[j];
if (*((int *)pTbl->address) != 0)
Since configTable is an array of structs, I'd want each iteration to have pTbl point to the entire struct from array element. Then with a pointer to that struct, I'd use the "->" notation to dereference that pointer, access member "address", which is also a pointer, so I'll cast it to be good then derefence it to get the integer value it is pointing to. That way, all data types are correct, the intention I believe is easy to understand, and no warnings from compiler or an error checker tool (for example Misra rule violations). I mean, is there some reason to do it like that? I don't mean to tell other more senior developers how to write their code (i.e. "you didn't write it like I would!") but this way seems like it could be undefined behavior, or at least bad practice.
Like I said, it works somehow. Hope this isn't too hard to follow. It's really easy to see in the actual code.
/* myFile.c */
int number0 = 0;
int number1 = 1;
int number2 = 2;
int number3 = 3;
typedef struct
{
void *addr;
int count;
} myTable_T;
const myTable_T configTable[4] = {
{ &number0, 10 },
{ &number1, 20 },
( &number2, 30 },
{ &number3, 40 }
};
void myRoutine()
{
const myTable_T *pTbl;
int j;
for (j = 0; j < 4; j++)
{
pTbl = configTable[j].addr;
if ((int) pTbl->addr != 0)
{
// do something
}
}
The lines that concern me are where pTbl is assigned, and then used in the condition. First, pTbl is a pointer to a struct myTable_T, but the right hand assignment is a void pointer. Then the next line does treat it as a struct still, as if struct member "addr" is actually another struct (it's not). How does that even work? It does function correctly. Also, even if the assignment to pTbl does actually return the struct pointer, doesn't the int cast actually cast away the void pointer? Because we need two dereferences, one for the pointer to the struct, one for the pointer to member addr.
I think at the least that pTbl can't access the second member in the struct. Later in the code, when the second member (count) is accessed, it's done directly from configTable, rather than through pointer pTbl. I think they tried to access it with pTbl but couldn't, so just went at it directly!
I'd expect to see those two lines something like:
pTbl = &configTable[j];
if (*((int *)pTbl->address) != 0)
Since configTable is an array of structs, I'd want each iteration to have pTbl point to the entire struct from array element. Then with a pointer to that struct, I'd use the "->" notation to dereference that pointer, access member "address", which is also a pointer, so I'll cast it to be good then derefence it to get the integer value it is pointing to. That way, all data types are correct, the intention I believe is easy to understand, and no warnings from compiler or an error checker tool (for example Misra rule violations). I mean, is there some reason to do it like that? I don't mean to tell other more senior developers how to write their code (i.e. "you didn't write it like I would!") but this way seems like it could be undefined behavior, or at least bad practice.
Like I said, it works somehow. Hope this isn't too hard to follow. It's really easy to see in the actual code.