struct arguments to function

J

Jack

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
//function body
}

void func2(struct B1* b)
{
//function body
}

void func3(struct B2* b)
{
//function body
}

int main(void)
{
struct B2 *b;
func1(b); //LINE2
func2(b); //LINE3
}

LINE2 is correct. LINE1 should work in this example because the func1's argument a is B2 type. How about LINE2?

Thanks.

Jack
 
B

Bart van Ingen Schenau

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 func3(struct B2* b);
void func1(struct A* a)
{
func3(a); //LINE1

The compiler can not accept this call, because func3 needs a pointer to a
struct B2, but is given a pointer to a struct A and there is no
(implicit) conversion defined between the two.

If you know that `a` always refers to the initial member of a struct B2,
then you could change that line to
func3((struct B2*)a);
//function body
}

void func2(struct B1* b)
{
//function body
}

void func3(struct B2* b)
{
//function body
}

int main(void)
{
struct B2 *b;
func1(b); //LINE2
The compiler can not accept this call, because func1 needs a pointer to a
struct A, but is given a pointer to a struct B2 and there is no implicit
conversion defined between the two.
You can use an explicit conversion (cast) here, because struct B2 has a
struct A as its first member:
func1((struct A*)b);
func2(b); //LINE3
The compiler can not accept this call, because func2 needs a pointer to a
struct B1, but is given a pointer to a struct B2 and there is no
conversion defined between the two.
}

LINE2 is correct. LINE1 should work in this example because the func1's
argument a is B2 type. How about LINE2?

None of the marked lines is correct.
Although you can freely convert between a pointer to a structure and a
pointer to the initial element of that structure, such conversions will
never be performed implicitly.
Additionally, you might know that func1's argument always refers to the
initial element of a struct B2, but the compiler can't know that.
Thanks.

Jack

Bart v Ingen Schenau
 
J

James Kuyper

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);
How about LINE2?

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.
 
J

Jack

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.






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;







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);






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);

Thanks. Even if using union, we still can not access B1's member,right? like:
((struct B1*)&u.b2)->type

Jack
 
J

James Kuyper

Could you please find a better newsreader - google groups double-spaced
your response, making it hard to read.

....
[my patch:]

Sorry - I got mixed up; "Musician" should be replaced with a floating
point initializer for u.b2.price, such as 3.21.
Thanks. Even if using union, we still can not access B1's member,right? like:
((struct B1*)&u.b2)->type

Well, yes. As I said above, there are serious restrictions on the use of
this approach. The only things you can access are u.b2.aa, u.b2.price,
u.b2.num and u.b1.a (which is the same as u.b2.aa).
Neither u.b1.type nor u.b1.num has been set to any particular value. The
memory that would have been used to store their values, if they been
given any, may currently be in use to store some other values (depending
upon how much padding the compiler inserts into struct B1 and struct B2,
and where).

Your question was very abstract - if it reflects an issue that's come up
while you were trying to solve some problem, there's a pretty good
chance that the right solution looks quite a bit different.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,153
Members
46,699
Latest member
AnneRosen

Latest Threads

Top