In the following code:
struct A
{
char* name;
int age;
}
struct B1
{
struct A a;
char * type;
int num;
}
struct B2
{
struct A aa;
double price;
int num;
}
void func1(struct A* a)
{
func3(a); //LINE1
At this point, there is no declaration of func3() in scope. In C99 or
later, a diagnostic is required. In C90, such code would be acceptable
in itself, but it would be considered as implicitly declaring func3() to
be a function taking a struct A* argument and returning an int. I've
never deliberately taken advantage of that implicit declaration, and
it's been a long time since I last accidentally used it, so I'm not sure
exactly how it worked. I don't have a copy of C90 to check - but I
believe that the implicit declaration should cause problems further
down, when the compiler discovers that the definition of func3() isn't
compatible with that implicit declaration.
I'll answer the rest of your message by assuming that you inserted a
function prototype for func3() before the first time that you called it.
//function body
}
void func2(struct B1* b)
{
//function body
}
void func3(struct B2* b)
{
//function body
}
int main(void)
{
struct B2 *b;
Note: at this point b is uninitialized. You need to add something like
the following code before making any use of the value of b or the
behavior of your code would be undefined, even if none of the other
problems were present:
struct B2 b2 = {{"John", 45}, 123456.78, 90};
b = &b2;
func1(b); //LINE2
func2(b); //LINE3
}
LINE2 is correct.
No, it is not. func1() requires an argument of type struct A*; you're
passing it an argument of type struct B2*, and there's no implicit
conversion between those two types. A diagnostic message is required,
and if your compiler didn't produce one, you need to find out how to
raise the warning level. One legitimate way to do what you want is
func1((struct A*)b);
That has well-defined behavior because the first member of a struct B2
has the type "struct A", but that's dangerous because no diagnostic is
required if the first member of struct B2 were not of that type, even
though the result of such a mis-match is likely to be very bad. The best
way to do something like that is
func1(&b->aa);
LINE1 should work in this example because the func1's argument a is B2 type.
No, it should not. The fact that you violated a constraint by calling
func1(b) doesn't cancel out the fact that you violated another
constraint by calling func3(a). The types don't match for either call,
so a conforming implementation of C must generate a diagnostic message,
and is not required to accept the code. If it does accept the code after
generating the message, that code is not required to work.
However, as a practical matter, if the code is accepted, it probably
will work - but you shouldn't rely upon that fact - you should write the
code correctly.
You can fix this problem by using:
func3((struct B2*)a);
I presume, since you marked LINE3, but didn't mention it, and you
mentioned LINE2 twice, that the second mention of LINE2 was a typo for
LINE3?
LINE3 is also a constraint violation, just like the others. Unlike the
others, there's no simple fix. There is a complicated fix, with serious
restrictions on how it can be used:
Since the first member of struct B1 has the same type as the first
member of struct B2, you can access that member using either type (see
section 6.5.3.2p6) - BUT only if those two types are members of the same
union, and only within the scope of the declaration of that union type,
and only if the object you're referring to is in fact a member of a
union of that type:
// This definition must be inserted before the definition of
// func2()
union my_union
{
struct B1 b1;
struct B2 b2;
};
// This can go inside of main()
union my_union u = {.b2 = {{"Paul", 36}, "Musician", 21}};
func2((struct B1*)&u.b2);
If you do something like that, func2() can safely access b->a, but it
can't safely access any other member of that structure. Even though both
struct types have a member named 'num', and it's the third member of
each struct, and has the same type in both structs, it's not safe to
access it; 6.5.3.2p6 allows access only to a common initial sequence of
struct members with compatible types - the fact that "type" and "price"
have different types terminates the common initial sequence.