Discussion in 'C Programming' started by Robbie Brown, Jan 20, 2014.

  1. Robbie Brown

    Robbie Brown Guest

    I'm working my way through the book

    "Understanding and using C pointers" by Richard Reese

    I have a question regarding types

    I have the following typedefs

    typedef int (*fptrOperation)(int,int);
    typedef int (*operation)(int, int);

    I then have an array of pointers of type fptrOperation

    fptrOperation operations[128] = {NULL};

    later I have the following code where I declare
    a variable of type operation and then assign a pointer
    of type fptrOperation to it.

    operation op;
    op = operations[opcode];

    This compiles and runs (gcc -std=c99)

    Given that I'm used to strongly typed languages this is causing me some

    I thought I understood typedefs, what am I missing.

    Robbie Brown, Jan 20, 2014
    1. Advertisements

  2. Robbie Brown

    Asaf Las Guest

    That means typedef evaluation to actual type is done at compile time
    before compile time type identification will be done.

    imho, this compiler behavior is not implementation specific but
    is explicitly specified by standard, but i don't have C99 standard to
    make quote from.

    Asaf Las, Jan 20, 2014
    1. Advertisements

  3. Robbie Brown

    Asaf Las Guest

    12:44 PM (2 minutes ago)
    That means typedef evaluation to actual type is done at compile time
    before compile time type check will be done.

    imho, this compiler behavior is not implementation specific but
    is explicitly specified by standard, but i don't have C99 standard to
    make quote from.


    edited ...
    Asaf Las, Jan 20, 2014
  4. In C, we can do this

    typedef float angle;

    Then we can apply the arithmetical operators, sizeof() and so on to angle.
    So to a large extent, we can toggle angle between flat and double or int,

    However at some point we're going to need to either convert to another type,
    or look at the bits. If only to print the angle out. So it's not easy to make
    the typedef into a genuine layer of abstraction between angles and a physical
    bit representation of angles. So C has got to allow mixed type operations,
    especially mixed operations on types which in fact map to identical underlying
    machine representations.
    Malcolm McLean, Jan 20, 2014
  5. The two types named fptrOperation and operation are the same. In C,
    typedef names are just aliases for the underlying types. All matters of
    type compatibility depend on the type itself, not on what it's called.

    I don't think of this as being directly related to strong or weak
    typing. Some (many?, most?) strongly typed languages will have a type
    alias like this, thought they will usually also have a way to define a
    new type, different to all others, regardless of the underlying type.
    If you need to do that in C you can define a one-element struct. It's a
    bit clumsy, so the gain has to be worth the clutter.

    Ben Bacarisse, Jan 20, 2014
  6. Robbie Brown

    Asaf Las Guest

    Asaf Las, Jan 20, 2014
  7. Robbie Brown

    Eric Sosman Guest

    Okay: Two different aliases for the same type.
    Most likely, you're missing the fact that a typedef does not
    define a new type. Rather, it defines a new alias for a type.
    For example, in

    typedef double Angle;
    typedef double Distance;

    .... `Angle' and `Distance' are not new and distinct types, but
    merely new names for `double'. All three of `Angle' and `Distance'
    and `double' are equivalent and interchangeable.
    Eric Sosman, Jan 20, 2014
  8. Robbie Brown

    Robbie Brown Guest

    Many thanks for all the responses, things are now much clearer.
    Robbie Brown, Jan 20, 2014
  9. Robbie Brown

    Kaz Kylheku Guest

    This has nothing to do with strength of typing. A C typedef name isn't
    a new type; it is a name for a type.

    The type is constructed by the syntax of the declaration:

    int (* ...)(int, int);

    this part of the syntax defines the type. Inside the declarator where
    I put ellipses above, there is a name which serves as the head of the
    declarator syntax:

    int (*name)(int, int);

    this name is introduced into the scope as a typedef name (because it's a
    typedef declaration), and is assigned to the type that was constructed in the
    declaration. But this name is not part of the type. Two typedef names for the
    same type are not distinct.

    C does have "strong typing" (in the sense of Pascal-like name equivalence); it
    just doesn't have strong typedef names. C's "strong typing" is exhibited by
    enum and struct tags, within a single translation unit.

    These are distinct, incompatible types even though they have the same

    struct student { int id; };
    struct employee { int id; };

    In C, struct types are considered for compatibility based on their tag, not
    their structure.

    Note that tags are not names introduced by a declaration; they are in the tag
    namespace. The declaration

    typedef struct student { int id;} student;

    introduces the tag "student" into the tag namespace and "student" into the
    ordinary namespace as a typedef name. (Note that in this regard C++ works a
    little bit differently, while preserving compatibility with C to a high

    Anyway, a struct student cannot be assigned to an employee; a pointer to a
    struct student cannot be passed to a function that expects a pointer to struct
    employee and so forth. They are distinct types because of their distinct tags.

    This rule is relaxed between translation units, however. In C the model
    of multiple compilation is that a translation unit has no access to any
    declaration information in another translation unit. Rather, the programmer
    must declare what is in another translation unit by providing declarations
    (usually deposited in a "header" file).

    In the case of a struct and union types, a translation unit can be compiled
    using one tag name, like "struct foo", and the declarations for that
    translation unit can use another name like "struct bar". This is compatible.

    What is going on here is structural compatibility between a declared type, and
    a compiled entity in a foreign translation unit about which no type information
    is available any more.
    Kaz Kylheku, Jan 20, 2014
  10. Not really.
    Tags in C are in a separate namespace, but both tag names and typedef
    names, and other ordinary names, are introduced by declarations.
    (Except implicit-old-int functions before C99. And yes C++ differs.)

    But within a t.u. only the same *declaration* is the same type,
    regardless of using the same tag (which is optional anyway).

    struct student { int id; } a;
    struct student { int id; } b; /* again in same scope is an error */
    int foo (){
    struct student { int id; } c; /* is a *different* type */

    One annoying problem caused by this is:
    void func (struct foo * p) ;
    /* struct is declared only in scope of the prototype */
    struct foo { int id; } bar;
    /* intended to be the argument to myfunc but: */
    int main (void){ func(&bar); return 0; } /* not compatible call */
    void func (struct foo * p){ } /* not compatible definition */
    You must either put the struct foo {...} before the function
    declaration using it, or put a forward declaration:
    struct foo; /* declares the tag but not yet contents */
    and if you use typedefs like
    typedef struct { int id; } Tstudent;
    typedef struct { int id; } Temployee;
    they are also distinct without using any tags. But:
    typedef struct { int id; } Tstudent, Tjunior;
    are two typedef names for the same type. So are:
    typedef struct { int id; } Tstudent;
    typedef Tstudent Tjunior;
    Not officially. n1569 6.2.7p1:
    If one is declared with a tag, the
    other shall be declared with the same tag. If both are completed
    anywhere within their
    respective translation units, then the following additional
    requirements apply: there shall
    be a one-to-one correspondence between their members such that each
    pair of
    corresponding members are declared with compatible types; if one
    member of the pair is
    declared with an alignment specifier, the other is declared with an
    equivalent alignment
    specifier; and if one member of the pair is declared with a name, the
    other is declared
    with the same name. For two structures, corresponding members shall be
    declared in the
    same order. For two structures or unions, corresponding bit-fields
    shall have the same

    In practice compilers don't vary struct layout for either tag OR
    member names, so variations in those 'accidentally' work.
    Further often several types use the same representation and work;
    e.g. on many (IL32LL64) systems
    struct x { int a; };
    struct y { long b; };
    in different t.u.s will actually work -- but are not guaranteed.
    In practice yes.
    David Thompson, Jan 26, 2014
  11. [...]

    N1256 is the best current draft for C99 (it includes the official C99
    standard with the three Technical Corrigenda merged into it).

    N1570 is the best current draft for C11; it's the last freely available
    committee draft before the official release of the ISO C11 standard.
    There are a handful of minor differences between N1570 and C11.

    Keith Thompson, Jan 27, 2014
  12. Robbie Brown

    Tim Rentsch Guest

    To be compatible, two structure types (or two union types) must
    have the same tag (or both have no tag), even for types declared
    in separate translation units. (Here "structure type" is meant
    in the sense of a declaration that includes a 'struct' type
    specifier, and similarly "union type".) A "struct foo" is never
    compatible with a "struct bar".
    Tim Rentsch, Feb 6, 2014
  13. Robbie Brown

    Tim Rentsch Guest

    What I think you mean to say is that different declarations in
    the same scope and using the same tag refer to the same type, but
    that type may have its content defined only once. (There are
    additional rules for declarations in different scopes.) In the
    example below both of the first two declarations refer to the
    same type.
    The third declaration refers to a new type because it defines
    the content of the type; if it did not define the content,
    it would refer to the same type as the first two declarations.
    Tim Rentsch, Feb 6, 2014
  14. I wasn't explaining the tag-only forms at all, but when they are
    included yes yours is the better description. (Formally there is a
    distinction between declaring the tag and declaring the type, but
    that's too subtle most of the time.)
    They both (try to) declare content for the same tag, which (as you
    just said) is a constraint violation. 'struct student b' would refer
    to the same type.
    No for the same reason.
    struct student c; /* would be the same type as a, if b absent */

    To be complete:

    struct student {int xx;} a = {0};
    //struct student {int xx;} ax; // error
    //struct student {int yy;} ay; // error
    struct student *ap = &a;
    struct teacher {int zz;} b = {0};

    int main(void){
    struct student c = a, *cp = &a; // same type as a

    struct student; // start scope for d
    struct student *dp; // same type as d but not a
    struct student {int xx;} d; dp = &d;
    //d = a; // incompatible type: error
    dp = &a; // incompatible pointer type

    struct teacher; // start scope for e
    struct teacher {int zz;};
    struct teacher e, *ep = &e;
    //e = b; // as above
    ep = &b; // as above

    return 0;
    David Thompson, Feb 9, 2014
    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.