Types in C

I

Ian Collins

Yes. But gmp is built first for the bootstrap phase. (At least... It
is in the version I maintain build scripts for. I assume the clever folks
at Code Sourcery know what they're doing.)

OK, that surprises me. I wonder why building a compiler requires gmp?
 
U

Uncle Steve

If they aren't defined buy programmers, who are they defined by?

The compiler writers. See my recent reply to Peter Seebach.
Well if you want to be pedantic, unions aren't aggregate types because
they can only contain one member at a time.

Let's expand the above for completeness:

A typedef may provide an alias to any existing type, built-in,
aggregate, union, composite or even incomplete.

There, did I leave anything out?

No, it seems very reasonable.
Have you ever used void*? What about a library with an opaque handle type?

void * is a non-specific pointer, with special semantics contrasted to
normal pointer types. It is not "incomplete". Perhaps my internal
representation of "incomplete type" is wrong. I labor under the
delusion that a structure defined with an element whose type is not
known is incomplete:

struct incomplete {
int foo;
struct bar something;
};

until the compiler encounters a definition of "struct bar", struct
incomplete is precisely that, and cannot be used to define anything.
This produces an error if there is no struct bar definition because
the compiler cannot guess what code to generate. Hence, it would
never be encountered at run-time. Please correct me if this is not
the case.
gmp has been around for a long time, well before inline function support
was common in C compilers (or even standardised).

I think they expect old platforms to use old versions of gmp.



Regards,

Uncle Steve
 
S

Seebs

OK, that surprises me. I wonder why building a compiler requires gmp?

My guess is for preprocessor arithmetic, which has to be implemented
reliably in the largest type available on the target, which may not be the
host. You can cross-compile from a 32-bit machine to a 64-bit, so the
preprocessor has to be able to do 64-bit math.

Using gmp is probably a lot simpler than writing another thing just like
it. :)

-s
 
U

Uncle Steve

The big issue is that a lot of code which assumes that int and long are
interchangeable tends to break.

I can see that, and if programmers don't check INT_MAX etc. it's
their own damn fault when their code breaks.
In practice, it's a great thing, because you don't actually *care* whether
something is the same size on all machines, you just care whether it's
working well.

Um, no. I care about the "size" of a type so I don't misuse it in the
program. If a counter rolls over because I assumed an int was 32 bits
wide, that's a problem. If I use an int type of known size, I'll be
much less likely to run into that problem.
I find very little need for int32_t, and very much for int and long.

That probably speaks to radically different coding styles.



Regards,

Uncle Steve
 
S

Seebs

Oh, no there aren't!

Well, I'll qualify it:

There are rules in C for defining things which the language calls
"types" and which participate fully in the general C rules about type
aliasing and type safety.
Not if we make a finer distinction between types.

That distinction is *within* the category of "types", though.
char, int, long, float, void *, etc. are /basic types/ fundamental to
the implementation of the C language (and arithmetic operations).
Compound types are aggregates of these basic types as well as compound
types. There's a fundamental difference you are ignoring given a
sufficient vague definition of 'type'.

No, I'm not ignoring that difference. I'm stating that it is a difference
between some types and other types; between basic types and derived types,
say. Or basic types and aggregate types.

But aggregate types are still types!
A real user-defined type would be something new like the complex
numbers extension in gcc.

No, that would be a new user-defined *basic* type. The word "type" has
a very rigorous meaning in C, and aggregate types are types, and user-defined
types exist. That only some kinds of types can be user-defined doesn't
change that.
gmp is well into version 4; I doubt it would compile where there is no
support for inline functions, but I have not verified this.

As pointed out: gmp is part of the bootstrap process for gcc. It compiles
on pretty old stuff, by necessity.

-s
 
S

Seebs

Um, no. I care about the "size" of a type so I don't misuse it in the
program. If a counter rolls over because I assumed an int was 32 bits
wide, that's a problem. If I use an int type of known size, I'll be
much less likely to run into that problem.

You're doing it wrong.

Don't know the size. Know the *minimum* size. You don't actually need
to know whether something is 32 bits, you just need to know that it's at
least big enough to hold some particular value. For all you know you're
getting a 64-bit type because that's what's fast on this hardware.

If you specify a specific type when a less specific type would do, you hurt
performance on a fair variety of hardware.

-s
 
I

Ian Collins

The compiler writers. See my recent reply to Peter Seebach.

So if I write struct Point { int x,y; }; do I become a compiler writer?

The distinction you attempt to draw in the other reply is an artificial
one that does not apply to C.
No, it seems very reasonable.

Good, that's a start.
void * is a non-specific pointer, with special semantics contrasted to
normal pointer types. It is not "incomplete".

See 6.2.5 p19.
Perhaps my internal
representation of "incomplete type" is wrong. I labor under the
delusion that a structure defined with an element whose type is not
known is incomplete:

struct incomplete {
int foo;
struct bar something;
};

It is, the compiler interprets "struct bar something;" as a forward
declaration of struct bar.
until the compiler encounters a definition of "struct bar", struct
incomplete is precisely that, and cannot be used to define anything.

Which is why we use a pointer member for incomplete types:

struct bar;

struct incomplete {
int foo;
struct bar* something;
};
This produces an error if there is no struct bar definition because
the compiler cannot guess what code to generate. Hence, it would
never be encountered at run-time. Please correct me if this is not
the case.

It is, but a pointer to an incomplete may be used.
 
S

Shao Miller

What do you think of the two definitions
(1) Functional (a type is an algorithm description)
(2) Conceptual (A type embodies a concept in the program)

And I think I am missing one:

(3) A type implies a set of operations allowed with it.

#1: Ironically, I'm not sure how this would apply to a function type or
an incomplete type.

I suppose for a function type, the algorithm could describe how
arguments are passed, given a calling convention.

Using "functional" might confuse a reader who might naively try to tie
"function type" to #1 while developing their understanding, even though
I doubt that's your intention.

#2: Sure. I'm not sure that that's a _distinguishing_ characteristic of
"type," though. I think there are a few other things which also embody
a concept in a program, including values.

enum fruit { apple, orange, banana, grapefruit };

#3: I like this one more than #1 and #2. :) A type is an attribute of
every expression, so operators can require certain types and/or can
behave in a particular manner, depending on the types of their operand
expressions.

"Type" is such a generic notion, in general, that to pin it down for
meaning in C, perhaps you'd have to build up some predicates which can
be said to be true or sometimes true of "type."

builtin, declared_where, declared, defined_where, defined,
function_or_object, complete_or_incomplete, qualifiers, qualified, size,
has_size, alignment, has_alignment, names_and_aliases, is_named, etc.

A type can also be [ab]used to accomplish other things, such as forcing
diagnostic messages or being used with 'sizeof' merely to carry a
constant value around.
 
S

Shao Miller

There are no "user defined" types in C. Structures and unions are
aggregates of basic types (and structures/unions). I believe
incomplete types are a (usually temporary) artifact of the compilation
process and you won't ever see them in a running program.

There are function types, too.

typedef struct {
char uncle[5];
} s_uncle;
typedef s_uncle * f_new_uncle(int, const char *, const s_uncle *);
static f_new_uncle new_uncle_unsafe;
f_new_uncle new_uncle;

That function type appears to be user-defined.
 
U

Uncle Steve

There are no "user defined" types in C. Structures and unions are
aggregates of basic types (and structures/unions). I believe
incomplete types are a (usually temporary) artifact of the compilation
process and you won't ever see them in a running program.

There are function types, too.

typedef struct {
char uncle[5];
} s_uncle;
typedef s_uncle * f_new_uncle(int, const char *, const s_uncle *);
static f_new_uncle new_uncle_unsafe;
f_new_uncle new_uncle;

That function type appears to be user-defined.

Sure, but a function definition is not a basic type. It's another
special case for type-checking the arguments and return value of
functions. Call it an aggregate because it bears more resemblance to
a structure than an int.

BTW, would you use, say, k_thompson as a variable identifier when you're writing
examples in reply to Keith Thompson? Just curious.



Regards,

Uncle Steve
 
U

Uncle Steve

You're doing it wrong.
Maybe.

Don't know the size. Know the *minimum* size. You don't actually need
to know whether something is 32 bits, you just need to know that it's at
least big enough to hold some particular value. For all you know you're
getting a 64-bit type because that's what's fast on this hardware.

If I define a variable as a 32-bit integer, by definition I know what
its range of values are. If I try to exceed that range, it is
programmer error. If I port my code to a different platform,
the typedefs for those types will get made so they are consisted with
the x86 port, if possible. I don't see how that can be bad.
If you specify a specific type when a less specific type would do, you hurt
performance on a fair variety of hardware.

I think platform specific code will (almost?) always be quicker than
generalized code that compiles and runs on multiple architectures
unmodified. I think you're going to have to show how your logic
works on this point.



Regards,

Uncle Steve
 
U

Uncle Steve

Well, I'll qualify it:

There are rules in C for defining things which the language calls
"types" and which participate fully in the general C rules about type
aliasing and type safety.



That distinction is *within* the category of "types", though.

Absolutely correct, but I think it may be an error to use the word
"type" to mean several different things: 'basic' types,
aggregates of basic types, enumerated types, unions, structures, and
function definitions. All these things have wildly different
semantics and varying syntax. They aren't all interchangable, and
therefore are differing entities. We say 'type', but the definition
changes depending on what we are discussing.
No, I'm not ignoring that difference. I'm stating that it is a difference
between some types and other types; between basic types and derived types,
say. Or basic types and aggregate types.

But aggregate types are still types!

I have no real argument there except to say that an aggregate type is
not the same thing as a basic type, even if the way they are used in a
program can be similar. For instance, everyone knows that the address
of an integer, float, structure, or function can be taken. Yet, I
cannot perform addition on functions. They are different things.
No, that would be a new user-defined *basic* type. The word "type" has
a very rigorous meaning in C, and aggregate types are types, and user-defined
types exist. That only some kinds of types can be user-defined doesn't
change that.

Again, there is a huge qualitative difference between defining a new
integer type and a structure type.
As pointed out: gmp is part of the bootstrap process for gcc. It compiles
on pretty old stuff, by necessity.

Which version of gmp does gcc use these days to support those old
platforms?



Regards,

Uncle Steve
 
U

Uncle Steve

So if I write struct Point { int x,y; }; do I become a compiler writer?

Perhaps, but only in your Special Place...
The distinction you attempt to draw in the other reply is an artificial
one that does not apply to C.

There's another one to look at as well, in which I attempt to clarify
the evident confusion over the terminology.
Good, that's a start.


See 6.2.5 p19.


It is, the compiler interprets "struct bar something;" as a forward
declaration of struct bar.


Which is why we use a pointer member for incomplete types:

struct bar;

struct incomplete {
int foo;
struct bar* something;
};


It is, but a pointer to an incomplete may be used.

I see the logic, as pointers ought to be the same size regardless of
the thing they point to. But of what use is it to define an
incomplete pointer type? All you can do is suppress warnings by
casting any assignment to a complete type that the compiler knows
about.



Regards,

Uncle Steve
 
I

Ian Collins

I see the logic, as pointers ought to be the same size regardless of
the thing they point to. But of what use is it to define an
incomplete pointer type? All you can do is suppress warnings by
casting any assignment to a complete type that the compiler knows
about.

I wrote "pointer to an incomplete type" (OK, I omitted "type"!).

A pointer to an incomplete type is frequently used as an handle type to
avoid exposing the type definition to the user. FILE* is the most
obvious example.
 
S

Seebs

If I define a variable as a 32-bit integer, by definition I know what
its range of values are. If I try to exceed that range, it is
programmer error. If I port my code to a different platform,
the typedefs for those types will get made so they are consisted with
the x86 port, if possible. I don't see how that can be bad.

It can be bad because you might end up on a machine where 32-bit types
are intrinsically slower than 64-bit types, so by mandating that ONLY
32 bits be used, you're slowing the program down.
I think platform specific code will (almost?) always be quicker than
generalized code that compiles and runs on multiple architectures
unmodified.
Sometimes!

I think you're going to have to show how your logic
works on this point.

See above. Imagine that you have something that you know only needs to
be 16 bits, so you specify i16 for it. You have an array of them. You're
on a machine where access on anything other than 64-bit boundaries adds
noticeable latency. Poof!

This kind of thing really does happen. There's a reason C gives minimum
ranges for the basic types.

Machine-specific code which does something that's stupid on a particular
machine is not faster than portable code which lets the compiler pick
something smart.

In general, the vast majority of code does not gain any performance from
trying to be more machine-specific, and the cases where it hurts, it hurts
BADLY.

-s
 
S

Shao Miller

51 AM, Uncle Steve wrote:
size_t may be exactly the same type as unsigned long,
since a typedef merely creates an alias for an existing type, not
a new and distinct type. plain char and signed char (for example)
are distinct in a way that size_t and unsigned long (for example)
are not.

True. Although, possibly confusingly, there are places where it seems
that the thing a typedef creates is itself then called "a type". The
wording is a bit fuzzy.

We all know what we mean, I think. You could say it several ways:

Typedef does not actually create types; instead, it creates
names which refer to existing types.

Typedef creates types, but those types are identical to existing
types and can be used interchangably with them.

How about the idea that typedefs create aliases for the basic built-in
types supplied by the language spec.?

A typedef may provide an alias to any existing type, built-in, user
defined or even incomplete.

There are no "user defined" types in C. Structures and unions are
aggregates of basic types (and structures/unions). I believe
incomplete types are a (usually temporary) artifact of the compilation
process and you won't ever see them in a running program.

There are function types, too.

typedef struct {
char uncle[5];
} s_uncle;
typedef s_uncle * f_new_uncle(int, const char *, const s_uncle *);
static f_new_uncle new_uncle_unsafe;
f_new_uncle new_uncle;

That function type appears to be user-defined.

Sure, but a function definition is not a basic type.

Ok, but now you've said "basic type" rather than "\"user defined\" types".

Beyond that, the second typedef in the example is not a function
definition. It includes the definition of a function type.

The standard definition in C99, if I'm not mistaken, includes:

"The meaning of a value stored in an object or returned by a function
is determined by the /type/ of the expression used to access it..."
It's another
special case for type-checking the arguments and return value of
functions. Call it an aggregate because it bears more resemblance to
a structure than an int.

Well, "aggregate" goes more like this, if I'm not mistaken:

"Array and structure types are collectively called /aggregate types/."

I think that the best argument against "user-defined types" might go
more like:

"\"user-defined\" does not appear in such-and-such standard of C."

I think that when you are using "aggregate," that perhaps you mean
"derived."

"Any number of /derived types/ can be constructed from the object,
function, and incomplete types, as follows..."
BTW, would you use, say, k_thompson as a variable identifier when you're writing
examples in reply to Keith Thompson? Just curious.

He has previously and kindly offered that using his first name is
acceptable, so I'd probably use that. Other possibilities include
"foo", "bar", "baz", "cat", "dog".
 
S

Shao Miller

I think you're going to have to show how your logic
works on this point.

Just intended as some kind words of advice as you appear to be
frequenting this newsgroup these days: Whatever you do, please avoid use
of the word "claims" with the poster who you replied to with the above.
You haven't made this "mistake," so far. :)
 
S

Shao Miller

Just intended as some kind words of advice as you appear to be
frequenting this newsgroup these days: Whatever you do, please avoid use
of the word "claims" with the poster who you replied to with the above.
You haven't made this "mistake," so far. :)

Oh, and please don't quote this. Thanks.
 
U

Uncle Steve

I wrote "pointer to an incomplete type" (OK, I omitted "type"!).

A pointer to an incomplete type is frequently used as an handle type to
avoid exposing the type definition to the user. FILE* is the most
obvious example.

Ah, well that makes sense. I understand that it's good practise to
insulate layers within the application to discourage users from
relying on private data fields which might be implementation specific,
arch specific, or which are moving targets in an actively developed
project.



Regards,

Uncle Steve
 
S

Seebs

Absolutely correct, but I think it may be an error to use the word
"type" to mean several different things: 'basic' types,
aggregates of basic types, enumerated types, unions, structures, and
function definitions. All these things have wildly different
semantics and varying syntax. They aren't all interchangable, and
therefore are differing entities. We say 'type', but the definition
changes depending on what we are discussing.

No, it doesn't. The word "type" denotes the category into which all
of those things fit.

It's like "number". The word "number" includes rationals and irrationals
and complexes and counting numbers... so what? It's a good word because
it lets us characterize the stuff they have in common.

All the "types" have things in common in terms of their meaning to the
C type system.
I have no real argument there except to say that an aggregate type is
not the same thing as a basic type,

That's right.
even if the way they are used in a
program can be similar. For instance, everyone knows that the address
of an integer, float, structure, or function can be taken. Yet, I
cannot perform addition on functions. They are different things.

Right. That is because function pointer types are not object pointer types.

But function types and object types still have underlying commonalities.
It is useful to refer to those commonalities in some way; we use the word
"type".
Again, there is a huge qualitative difference between defining a new
integer type and a structure type.

Sure.

But they are differences *within* the concept of "defining a type".
Which version of gmp does gcc use these days to support those old
platforms?

gcc 4.4 requires GMP 4.1+ and MPFR 2.3.2+.

Just to clarify: My day job is toolchain tech lead. The actual hard
compiler stuff is outsourced to Code Sourcery (who are awesome), but I do
maintain a certain basic familiarity with compiler build stuff. Long
story short, the core tools like this are only relatively recently (I
think it's a gcc 4 decision) willing to assume that they have access to a
compiler which supports prototypes.

-s
 

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
474,073
Messages
2,570,537
Members
47,195
Latest member
RedaMahuri

Latest Threads

Top