struct arguments to function

Discussion in 'C Programming' started by Jack, Dec 14, 2012.

  1. Jack

    Jack Guest

    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
    Jack, Dec 14, 2012
    #1
    1. Advertising

  2. On Thu, 13 Dec 2012 23:09:47 -0800, Jack wrote:

    > 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
    Bart van Ingen Schenau, Dec 14, 2012
    #2
    1. Advertising

  3. Jack

    James Kuyper Guest

    On 12/14/2012 02:09 AM, Jack wrote:
    > 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.
    --
    James Kuyper
    James Kuyper, Dec 14, 2012
    #3
  4. Jack

    Jack Guest

    On Friday, December 14, 2012 5:01:18 AM UTC-8, James Kuyper wrote:
    > On 12/14/2012 02:09 AM, Jack wrote:
    >
    > > 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);


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

    Jack
    Jack, Dec 16, 2012
    #4
  5. Jack

    James Kuyper Guest

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

    On 12/15/2012 07:18 PM, Jack wrote:
    > On Friday, December 14, 2012 5:01:18 AM UTC-8, James Kuyper wrote:
    >> On 12/14/2012 02:09 AM, Jack wrote:
    >>
    >>> 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;

    ....
    [my patch:]
    >> struct B2 b2 = {{"John", 45}, 123456.78, 90};
    >> b = &b2;


    ....
    >>> func1(b); //LINE2
    >>> func2(b); //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}};


    Sorry - I got mixed up; "Musician" should be replaced with a floating
    point initializer for u.b2.price, such as 3.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


    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.
    --
    James Kuyper
    James Kuyper, Dec 16, 2012
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Chris Fogelklou
    Replies:
    36
    Views:
    1,345
    Chris Fogelklou
    Apr 20, 2004
  2. Ole
    Replies:
    4
    Views:
    585
    Michael Wojcik
    Oct 26, 2004
  3. Neo
    Replies:
    10
    Views:
    640
    sushant
    Jan 20, 2005
  4. tutmann
    Replies:
    4
    Views:
    425
  5. jmborr
    Replies:
    1
    Views:
    397
    Stargaming
    Nov 3, 2007
Loading...

Share This Page