Types

R

Robbie Brown

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

I thought I understood typedefs, what am I missing.

Thanks
 
A

Asaf Las

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
confusion.
I thought I understood typedefs, what am I missing.
Thanks

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
 
A

Asaf Las

12:44 PM (2 minutes ago)
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
confusion.
I thought I understood typedefs, what am I missing.
Thanks

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.

Asaf

edited ...
 
M

Malcolm McLean

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

I thought I understood typedefs, what am I missing.
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,
transparently.

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

Ben Bacarisse

Robbie Brown said:
I have the following typedefs

typedef int (*fptrOperation)(int,int);
typedef int (*operation)(int, int);
fptrOperation operations[128] = {NULL};
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 confusion.

I thought I understood typedefs, what am I missing.

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.

<snip>
 
E

Eric Sosman

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

Okay: Two different aliases for the same type.
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
confusion.

I thought I understood typedefs, what am I missing.

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

Robbie Brown

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

I thought I understood typedefs, what am I missing.

Thanks


Many thanks for all the responses, things are now much clearer.
 
K

Kaz Kylheku

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

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

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
structure:

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
degree.)

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

David Thompson

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: <snip>
Yes.

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.
Not really.
These are distinct, incompatible types even though they have the same
structure:

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
degree.)
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 */
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.
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;
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.
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
widths.

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

In practice yes.
 
K

Keith Thompson

David Thompson said:
Not officially. n1569 6.2.7p1:
[...]

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.

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
 
T

Tim Rentsch

Kaz Kylheku said:
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 structure:

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 degree.)

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.

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".
 
T

Tim Rentsch

David Thompson said:
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).

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.
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 */
}

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

David Thompson

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

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.)
additional rules for declarations in different scopes.) In the
example below both of the first two declarations refer to the
same type.
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.
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.

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

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

Similar Threads

C99 integer types 24
Types in C 117
Compatible types and structures 2
pointer to array types 41
complex declarations 23
Linux: using "clone3" and "waitid" 0
Incomplete types 21
dereferencing function pointer types 9

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top