Could you explain this typedef to me?

Discussion in 'C Programming' started by fl, Feb 2, 2014.

  1. fl

    fl Guest

    Hi,
    I am puzzled by the following typedef under the dot line. It has two MsgObj. Then it also has a pointer definition. I think that the pointer can be defined as:

    MsgObj *Msg;

    Could you explain the part on MsgObj to me?


    Thanks,





    .................
    typedef struct MsgObj {
    QUE_Elem elem; /* first field for QUE */
    Int id; /* writer task id */
    Char val; /* message value */
    } MsgObj, *Msg;
     
    fl, Feb 2, 2014
    #1
    1. Advertisements

  2. fl

    Robbie Brown Guest


    Well I'm taking my life in my hands here as I'm a beginner myself.
    This provides two 'types' allowing you to do the following

    MsgObj mo; //allocate an instance of MsgObj on the stack

    you can now do

    mo.id = 99;

    or

    Msg m = malloc(sizeof(m)) //allocate an instance of MsgObj on the heap
    and assign the pointer to m

    now you can do

    m -> id = 89;

    Haven't quite figured out *why* you might want to do this yet but I
    tested it and it seems to work (notice I don't add 'as expected' :)
     
    Robbie Brown, Feb 2, 2014
    #2
    1. Advertisements

  3. fl

    Robbie Brown Guest

    Actually I think that should be Msg m = malloc(sizeof(*m))
     
    Robbie Brown, Feb 2, 2014
    #3
  4. fl

    Eric Sosman Guest

    This declares a `struct MsgObj' type, and the elements that
    make it up. It also declares `MsgObj' as an alias for that type;
    hereafter, `MsgObj' and `struct MsgObj' mean exactly the same
    thing, and can be used interchangeably. Finally, it declares
    `Msg' as an alias for a pointer type that can also be written
    as `MsgObj*' or `struct MsgObj*'.

    The reason that `MsgObj' can be an alias for `struct MsgObj'
    (and not be some kind of infinitely recursive nonsense) is that
    struct tags are in a different "name space" than other identifiers.
    In `struct MsgObj' the compiler knows that the thing after `struct'
    has to be a struct tag, so it only looks for those. But when the
    compiler sees `MsgObj' all by itself it looks among the ordinary
    identifiers and not among the tags. So the two `MsgObj' uses are
    distinct, just as `John Smith' and `John Jones' are distinct even
    though both are `John'.

    The C language actually has four kinds of name spaces:

    - statement labels,

    - the tags of struct, union, and enum types,

    - the members of a struct or union (each struct or union
    type has its own name space for its members),

    - everything else: variable names, function names, etc.

    The context indicates which name space a particular identifier
    belongs to: for example, in `goto StIves' the identifier `StIves'
    must belong to the name space of labels. Each identifier is
    unique *within its name space*, but may be spelled the same as
    an identifier in a different name space -- that's why `MsgObj'
    (in the "everything else" space) is distinct from `struct MsgObj'
    (in the tag space). Identical spellings don't confuse the
    compiler, however much they may confuse a human reader.
     
    Eric Sosman, Feb 2, 2014
    #4
  5. Keep in mind that a typedef does not create a new type; it merely
    creates a new name for an existing type.

    The above declaration defines three things:

    1. A structure type named "struct MsgObj";

    2. An alias for that structure type, named "MsgObj"; and

    3. An alias for the type "struct MsgObj*" (pointer to struct MesObj)
    named "Msg".

    Defining typedefs for structures is a common practice, but it's not
    actually necessary. My own personal preference, unless the type is
    meant to be opaque (i.e., code that uses it shouldn't rely on names of
    the members) is just to define the type as "struct MsgObj" and refer to
    it by that name. But a lot of programmers feel that it's worthwhile to
    add a typedef so that the type has a one-word name. Whatever your own
    preferences, you'll need to deal with both styles in code written by
    others.

    Defining a typedef for a pointer type is widely considered to be a bad
    idea. It's often better to keep the pointerness of the type explicit.
    So even if you have a typedef for the structure, if you want to define a
    pointer object you'd still write:

    MsgObj *foo;

    which means you can tell at a glance that foo is a pointer. But again,
    you'll likely have to deal with code that uses typedefs for pointers.

    A typedef declaration's syntax is very similar to the syntax of an
    object declaration. Adding the "typedef" keyword means you're defining
    a typedef name rather than an object name. So this:

    struct MsgObj {
    /* ... */
    } foo, *bar;

    defines foo as an object of type struct Msgobj and bar as an object of
    type struct MsgObj*. Add the typedef keyword:

    typedef struct MsgObj {
    /* ... */
    } foo, *bar;

    and foo and bar are defined as aliases for those same types rather than
    as objects of those types. Syntactically, "typedef" is treated as if it
    were a storage class specifier like "static" or "extern". It was
    defined this way for the sake of convenience ("typedef" was a relatively
    late addition to the language).
     
    Keith Thompson, Feb 2, 2014
    #5
  6. fl

    Robbie Brown Guest

    snip

    Loads of excellent advice here.
    For some reason however I'm less than confident I fully understand. This
    is because I have now suspended all expectation.

    I'm going to check my understanding, hopefully someone will put me
    straight if I'm wrong. (I have a thick skin).

    Given the following code

    typedef struct Foobar{
    int a;
    int b;
    }Foobar, *fbar;

    struct Barbaz{
    int a;
    int b;
    }Barbaz, *bbaz;

    //I can do

    Foobar fb;
    fb.a = 1;

    //but I can't do

    Foobar.a = 1;

    //I can do

    fbar fbp = malloc(sizeof *fbp);

    //but I can't do

    fbar = malloc(sizeof *fbar);

    //I can do

    Barbaz.a = 2;

    //but I can't do

    Barbaz bb;

    //I can do

    bbaz = malloc(sizeof *bbaz);

    //but I can't do

    bbaz bbp = malloc(sizeof *bbp)

    My compiler (gcc version 4.6.3 64 bit Linux) seems to agree with me but
    it's agreed with me in the (recent)past and I've still been wrong which
    I have to say is a new experience.

    Thank you
     
    Robbie Brown, Feb 3, 2014
    #6
  7. fl

    BartC Guest

    I've been using C for a while but I've been puzzled by these. I didn't even
    know you could have a list of things after a typedef.
    Here you're defining (if my understanding is correct):

    - Foobar as an alias for the type 'struct Foobar'
    - fbar as an alias for the type 'pointer to struct Foobar'

    (The latter I understand isn't considered good practice. Just use Foobar*
    when you want to express a pointer to the type.)
    Here you're defining a struct Barbaz (which happens to be compatible with
    struct Foobar above, however C will see it as different), and variable
    Barbaz of type 'struct Barbaz', and a variable bbaz of type 'pointer to
    struct Barbaz'.

    That's fine. But you're using Foobar as both a struct name, and typedef
    name, and Barbaz as both a struct name, and variable. C allows you to do
    this, but that this mean that people will find it easy to read!

    This is just my personal preference, but I would write these as:

    typedef struct {
    int a,b;
    } Foobar;

    typedef struct {
    int a,b;
    } Barbaz;

    Then I can just write:

    Foobar fp;
    Foobar *fbp;

    Barbaz ..... (here I'd use any variable name that isn't 'Barbaz'!)
    Barbaz *bbaz;

    (Or you can drop the typedef but then you'd have to write struct Foobar and
    struct Barbaz everywhere.)
     
    BartC, Feb 3, 2014
    #7
  8. fl

    James Kuyper Guest

    Actually, you can do that - the C standard never prohibits you from
    writing any code - all it does is tell you what a conforming
    implementation of C is, and is not, required to do when processing such
    a program.
    That code does violate a constraint. That means that there is only one
    requirement: a diagnostic message must be issued. The program need not
    be accepted, but it also need not be rejected. If it is accepted, and
    the program is executed, the behavior of that entire program is
    undefined, which means that the standard does not impose any
    requirements on that behavior.

    The constraint that has been violated requires that the left operand of
    a member-selection operator '.' be an lvalue referring to an object of
    struct or union type. Foobar is an alias for a struct type, it is not
    the name of a particular object of that type, so it does not qualify as
    an lvalue.
     
    James Kuyper, Feb 3, 2014
    #8
  9. fl

    James Kuyper Guest

    Yes, C will see them as two different types. More bizarrely, unless they
    are defined in different translation units, C will see them as
    incompatible types, which means that struct Barbaz cannot be used in any
    context where a type compatible with struct Foobar is required.

    I think the main purpose of this rule is to discourage duplicate
    definitions like these. If you want two different type names to identify
    compatible types, make one of them a typedef for the other, which makes
    the connection between them explicit.

    The exception for definitions in different translation units is needed,
    because "All declarations of structure, union, or enumerated types that
    have the same scope and use the same tag declare the same type."
    Definitions in different translation units are not in the same scope,
    and therefore cannot have the same type, even if they are identical.
    Allowing such types to be compatible, even though they are not the same
    type, and allowing compatible types to be used interchangeably, is the C
    standard's solution to this dilemma. Other solutions are possible, such
    as modifying the definition of "same type".
     
    James Kuyper, Feb 3, 2014
    #9
  10. fl

    Robbie Brown Guest

    Well I don't want to labor the point but my compiler won't produce an
    executable if I write Foobar.a = 1. I suppose that means that this
    particular version of this particular compiler doesn't accept this
    particular code.

    It seems bizarre to me that there is a situation where this code might
    be 'accepted' and even more bizarre that an executable might be
    produced, diagnostic message or not. It's almost as if this language has
    been designed to be as opaque as possible. What possible reason can
    there be to produce an executable that contains code that is quite
    obviously wrong. Very strange.
     
    Robbie Brown, Feb 3, 2014
    #10
  11. fl

    James Kuyper Guest

    Correct. There's only one construct a program can contain which requires
    a conforming implementation of C to reject it: a #error directive that
    survives conditional compilation. Note that, ironically, such a program
    MUST be correct written to ensure rejection. Any error in the
    preprocessor directives that determine whether the code is conditionally
    compiled would give the implementation the freedom to accept the code.
    The same is true of any syntax errors in the #error directive itself.
    No, that's what the diagnostic message is for. If you don't want to
    wander off the charted path, beware the warning signs that indicate that
    you have done so.
    The key point is that the code is not "obviously wrong" - it might be
    perfectly correct code for an extended version of C. The fact that the
    standard leaves the behavior undefined gives fully conforming
    implementations the freedom to provide their own definition of the
    behavior, as an extension. There are other ways to implement extensions:
    an option that renders the implementation non-conforming, or a #pragma
    that allows for arbitrary implementation-defined behavior. However,
    providing defined behavior for constructs that are syntax errors or
    constraint violations is one popular ways of implementing extensions.
     
    James Kuyper, Feb 3, 2014
    #11
  12. fl

    Robbie Brown Guest

    OK, well, you learn something new etc etc

    snip

    Hmm, I don't understand this.

    If I have the following

    typedef struct{
    long x;
    long y;
    }AArdvark;

    struct{
    char *cp1;
    char *cp2;
    }Whoami;

    then I can do

    AArdvark a;
    a.y = 8;

    AArdvark *ap = malloc(sizeof *ap);
    ap -> y = 9;

    but I can't do

    struct Whoami wai;
    struct Whoami *pwai = malloc(sizeof *pwai);

    I get

    error: storage size of ‘wai’ isn’t known
    error: dereferencing pointer to incomplete type

    Nor can I do

    struct Whoami.cp1 = "foo";

    as I get

    error: expected identifier or ‘(’ before ‘.’ token

    Am I missing something ?

    [You said "(Or you can drop the typedef but then you'd have to write
    struct Foobar and struct Barbaz everywhere.)"]

    Thanks
     
    Robbie Brown, Feb 3, 2014
    #12
  13. fl

    BartC Guest

    Sorry, I overlooked the fact that if you don't typedef your struct
    definitions, then you have to write them like this (if you want to refer to
    the type in another place):

    struct Whoami{
    char *cp1;
    char *cp2;
    };

    This defines a struct with the tagname 'Whoami', which you can then use as a
    type using 'struct Whoami'. With the syntax you used, you create an
    anonymous struct, used to declare a variable 'Whoami'.

    (There were good reasons why I tend to stick to certain patterns. C structs,
    typedefs, tagnames, and variable names, which sometimes have the same names,
    do get very confusing!)
     
    BartC, Feb 3, 2014
    #13
  14. Can you describe what this does? You telling me what you think this
    does will help me a lot.

    Do you know what a struct tag is?
    How did you try to find out for yourself? Are you just typing random
    things to see what happens of do you have a book or C reference to hand
    that you are learning from? You can find the syntax of C in lots of
    places on the internet, and this would have shown you that the above is
    not a syntactically valid sequence of tokens.
    A book on C would be my guess.
     
    Ben Bacarisse, Feb 3, 2014
    #14
  15. fl

    Robbie Brown Guest

    Well it doesn't work and I was responding to another respondent who
    suggested it might and yes, I'm fully aware of what a struct tag is
    thank you.
    I tried to compile it
    I was responding to another respondent who suggested it might work
    Again, I was responding to another respondent who suggested it might work.
    Then I suggest you don't take up gambling for a living
     
    Robbie Brown, Feb 3, 2014
    #15
  16. fl

    Eric Sosman Guest

    This says "There is a tagless (anonymous) struct type with
    two elements, and `AArdvark' is an alias for that type." You
    can now declare variables and pointers and such by using the
    name `AArdvark' -- in fact, that's the only way you can declare
    them, because there's no struct tag you could use.
    This says "There is a tagless struct type with two elements,
    and `Whoami' is a variable of that type." Since the struct type
    has no tag and there is no alias for it, there is no way you can
    declare any more instances of it; `Whoami' is the one and only
    instance of this type.

    (Well, I suppose you could use malloc() to get a similarly-
    sized piece of storage and then memcpy() to copy the content of
    `Whoami' into it, but you couldn't actually do much with that
    allocated storage until you copied it back into `Whoami' again.)
    Right. No `struct Whoami' has been declared. There's a
    variable named `Whoami', and that variable is an instance of a
    struct type, but there's no `struct Whoami'.

    It is legal in C to mention a struct (or union) type without
    declaring it; such a type is called "incomplete" until and unless
    the details are filled in later. But until the details become
    available, there are limitations on what you can do with such a
    type: You can't make a variable of that type (because the compiler
    doesn't know howbig it is), you can't apply `sizeof' to it (ditto)
    you can't copy one instance to another by assignment (ditto), and
    although you can form pointers to the type, you can't use them to
    get at the unknown insides. This strange state of affairs actually
    turns out to be useful when a library wants to use a type that is
    "opaque" to the calling code. Much of <stdio.h> traffics in `FILE*'
    pointers, but when you call fprintf() you neither know nor care
    what's inside a `FILE'. (The example isn't perfect because there's
    a strange technicality -- possibly a mistake -- in the C Standard
    that forbids an incomplete `FILE', but the intent is the same.)
    There are two things wrong here. First, `struct Whoami' is
    a type name, not a variable of that type: You can't assign a
    value to a type, but only to an instance. Second, `struct Whoami'
    is "incomplete" as described above, so the compiler has no idea
    of what the type of it's `cp1' field might be, where it's positioned
    inside the struct, nor even whether a `cp1' field exists!
     
    Eric Sosman, Feb 3, 2014
    #16
  17. fl

    Robbie Brown Guest

    Actually, I think I just got it wrong, I tried various things but
    obviously not the one that mattered ... sigh

    struct{
    char *cp1;
    char *cp2;
    }Whoami;

    then I *can* do

    Whoami.cp1 = "foobarbaz";

    can't think why I didn't try that.

    So now I appear to have 4 different ways to declare/define a struct.
    I wonder how many more there.

    Variety is the spice of life etc.
     
    Robbie Brown, Feb 3, 2014
    #17
  18. But you have no idea what it does, or you do have an idea but don't want
    to answer questions, or what? You will get better explanation of you
    choose to reveal just a hint or two about your confusion.

    I don't think you are a *fully* aware what a struct tag is or you would
    not have written the object definition that you wrote (and snipped).
    Of course I saw that but the BartC didn't suggest you write what you
    wrote. If you choose not to explain anything about how you think about
    this stuff, it's hard to offer a helpful explanation.
    Well that's a start. What C book is it? What is it about its
    explanation of struct types that is not clear?
     
    Ben Bacarisse, Feb 3, 2014
    #18
  19. fl

    James Kuyper Guest

    That's because you have not defined any type named "struct Whoami".
    You've defined an object of anonymous struct type named Whoami.
    The keyword struct is not allowed in that context; I'm not sure why you
    would expect it to be. Remove that keyword and the code should work.
    He meant that you could write

    struct Foobar { ... };

    rather than

    typedef struct {... } Foobar;

    but that if you did such a rewrite, then in every place where his code
    had "Foobar" other than the struct definition, you'd have to replace it
    with "struct Foobar".
     
    James Kuyper, Feb 3, 2014
    #19
  20. fl

    Eric Sosman Guest

    I'm not sure what your "4 different ways" are. You can
    declare a struct type with or without a tag, you can declare
    it with or without its innards ("complete" and "incomplete"
    types), but that doesn't amount to four ways because one of
    the combinations (tagless incomplete struct) is invalid. Also,
    you can use typedef to create aliases for the struct type and
    for types (like pointers) derived from it; you can do that in
    the same declaration as the struct itself or (if there's a tag)
    in a separate declaration.
    I get the impression that you're floundering, which suggests
    to me that you need to go back and review the whole notion of
    "type" in C. Perhaps it would do you good to stay away from
    typedef altogether for a while, as it seems to be confusing you
    at this point.
     
    Eric Sosman, Feb 3, 2014
    #20
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.