Types in C

J

Joe Pfeiffer

Keith Thompson said:
Yes, parentheses are heavily overloaded in C.

And overused :)
The "it's not a function call" argument is why I distinguish between
function calls and if/while/for by whitespace:

if (cond) ...
while (cond) ...
func(arg);

In my own style, a function call has the '(' immediately following the
function name; adding a space between 'if' and '(' makes it look less
like a function call. And I've been trying to develop the habit of
writing "sizeof (int)" rather than "sizeof(int)".

I've always followed the same convention, but never really thought about
why I adopted it.
 
M

Michael Press

Seebs said:
More importantly:

gmp is used by gcc.

gcc has a strictly dogmatic requirement that you don't need a very good
or modern compiler to build it.

What is the dogma?
 
M

Marcin Grzegorczyk

jacob said:
[...]
A C type can be either a function type, an incomplete type or an object
type.

Recently the Committee agreed with Clive Feather that completeness is a
scoped property of object types (there are no incomplete function types,
after all), and the latest C1X draft says the following under 6.2.5
("Types"):

# Types are partitioned into object types (types that describe objects)
# and function types (types that describe functions). At various points
# within a translation unit an object type may be incomplete (lacking
# sufficient information to determine the size of objects of that type)
# or complete (having sufficient information).

I, too, believe it is a clearer way of describing how the C type system
works. (For Clive's rationale, see the WG14 document N1439).
 
J

jacob navia

Le 30/05/11 22:42, Marcin Grzegorczyk a écrit :
jacob said:
[...]
A C type can be either a function type, an incomplete type or an object
type.

Recently the Committee agreed with Clive Feather that completeness is a
scoped property of object types (there are no incomplete function types,
after all), and the latest C1X draft says the following under 6.2.5
("Types"):

# Types are partitioned into object types (types that describe objects)
# and function types (types that describe functions). At various points
# within a translation unit an object type may be incomplete (lacking
# sufficient information to determine the size of objects of that type)
# or complete (having sufficient information).

I, too, believe it is a clearer way of describing how the C type system
works. (For Clive's rationale, see the WG14 document N1439).

After reading N1439 I would tend to agree with what it says but
where should go the void type?

It is void an object type?

What was easy to understand in the original classification is that void
had a natural neighborhood: incomplete types.

I deduce (indirectly) that Mr Cleaver thinks void is an object type
because he says:

6.3.2.1#1
Change “an object or incomplete type other than void” to “an object
type other than void”.

If "an object type other than void" is accepted it would mean that
void is an object type.

The problem is that N1429 is difficult to read because it only
refers to what should be patched and doesn't display the patched
text.

In any case thanks a lot for taking the time to answer.

jacob
 
S

Shao Miller

jacob said:
[...]
A C type can be either a function type, an incomplete type or an object
type.

Recently the Committee agreed with Clive Feather that completeness is a
scoped property of object types (there are no incomplete function types,
after all), and the latest C1X draft says the following under 6.2.5
("Types"):

# Types are partitioned into object types (types that describe objects)
# and function types (types that describe functions). At various points
# within a translation unit an object type may be incomplete (lacking
# sufficient information to determine the size of objects of that type)
# or complete (having sufficient information).

I, too, believe it is a clearer way of describing how the C type system
works. (For Clive's rationale, see the WG14 document N1439).

Though I think the obsolescence under the N1494's 6.11.6p1 is
intuitively akin to an "incomplete function type," though such a term is
not defined in a standard sense, perhaps.
 
S

Shao Miller

Le 30/05/11 22:42, Marcin Grzegorczyk a écrit :
jacob said:
[...]
A C type can be either a function type, an incomplete type or an object
type.

Recently the Committee agreed with Clive Feather that completeness is a
scoped property of object types (there are no incomplete function types,
after all), and the latest C1X draft says the following under 6.2.5
("Types"):

# Types are partitioned into object types (types that describe objects)
# and function types (types that describe functions). At various points
# within a translation unit an object type may be incomplete (lacking
# sufficient information to determine the size of objects of that type)
# or complete (having sufficient information).

I, too, believe it is a clearer way of describing how the C type system
works. (For Clive's rationale, see the WG14 document N1439).

After reading N1439 I would tend to agree with what it says but
where should go the void type?

It is void an object type?

Yes. But it can never be completed.
What was easy to understand in the original classification is that void
had a natural neighborhood: incomplete types.

I deduce (indirectly) that Mr Cleaver

(Nit-pick: Mr. Clive Feather.)
thinks void is an object type
because he says:

6.3.2.1#1
Change “an object or incomplete type other than void” to “an object
type other than void”.

If "an object type other than void" is accepted it would mean that
void is an object type.

Agreed.

The problem is that N1429 is difficult to read because it only
refers to what should be patched and doesn't display the patched
text.

In any case thanks a lot for taking the time to answer.

In the N1494 draft, we have 6.2.5p19:

"The void type comprises an empty set of values; it is an incomplete
object type that cannot be completed."

Similarly:

typedef char a_incomplete[];

could never be completed, if I'm not mistaken.
 
T

Tim Rentsch

jacob navia said:
[snip]

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)

(2) is not definitional. It might be appropriate to
discuss this idea as part of how/why different types
are used, but it should not be part of defining what a
type is.

(1) has more of a definitional flavor, but the particulars
are wrong in the context of C. Types in C are actually
fairly complicated beasts, but in essence a type is a
set of abstract values (the carrier set of the type) and
a mapping (in the mathematical sense) between the abstract
values and how those values are represented within the
computer. (Types in C also include an identity so we can
talk about two types being "the same type" or "different
types" even when the carrier sets and the mappings are the
same.) The relationship between abstract values and their
representations is better and more usually expressed as a
mapping, not by giving an algorithm.

And I think I am missing one:

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

This would be different than (1) since (1) refers to calculating
the "value" stored in a type.

I am sure (3) is missing but can't really identify it yet as
definitely distinct of (1).

Here again this is not definitional. Certainly it is true
that certain operations will work with certain types (or
certain combinations of types), but types have meaning
whether operations that work on those types exist or not.
The relationship between types and operations should be
discussed separately from explaining the idea of a type
and defining what a type is.
 
J

jacob navia

Le 31/05/11 09:40, Tim Rentsch a écrit :
jacob navia said:
[snip]

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)

(2) is not definitional. It might be appropriate to
discuss this idea as part of how/why different types
are used, but it should not be part of defining what a
type is.

Yes, I have dropped that, replacing it with a "format",
not an algorithm. "int" would be a format (sign bit,
value, two's complement, etc)
(1) has more of a definitional flavor, but the particulars
are wrong in the context of C. Types in C are actually
fairly complicated beasts, but in essence a type is a
set of abstract values (the carrier set of the type) and
a mapping (in the mathematical sense) between the abstract
values and how those values are represented within the
computer. (Types in C also include an identity so we can
talk about two types being "the same type" or "different
types" even when the carrier sets and the mappings are the
same.) The relationship between abstract values and their
representations is better and more usually expressed as a
mapping, not by giving an algorithm.

Interesting. You would say then that int is a subset of the
natural integers from INT_MIN to INT_MAX. That would be the
mathematical definition.

Character strings would be

A character string would be a sequence of integers from
CHAR_MIN to CHAR_MAX of undetermined length followed by a zero.

Floating point would be a sequence of integers too:
boolean sign
integer exponent (possibly biased)
integer mantissa

Is that what you mean?

And what about
struct customer{char *name;double balance; }; ???
Here again this is not definitional. Certainly it is true
that certain operations will work with certain types (or
certain combinations of types), but types have meaning
whether operations that work on those types exist or not.


Well, operations have meaning too: 2+2 has a very concrete meaning...
The relationship between types and operations should be
discussed separately from explaining the idea of a type
and defining what a type is.

IF we assume a mathematical description of a type. It can be
done for some primitive types but not with user defined types.


In any case thanks for your answer, it is an interesting viewpoint.
 
K

Keith Thompson

Shao Miller said:
jacob said:
[...]
A C type can be either a function type, an incomplete type or an object
type.

Recently the Committee agreed with Clive Feather that completeness is a
scoped property of object types (there are no incomplete function types,
after all), and the latest C1X draft says the following under 6.2.5
("Types"):

# Types are partitioned into object types (types that describe objects)
# and function types (types that describe functions). At various points
# within a translation unit an object type may be incomplete (lacking
# sufficient information to determine the size of objects of that type)
# or complete (having sufficient information).

I, too, believe it is a clearer way of describing how the C type system
works. (For Clive's rationale, see the WG14 document N1439).

Though I think the obsolescence under the N1494's 6.11.6p1 is
intuitively akin to an "incomplete function type," though such a term is
not defined in a standard sense, perhaps.

For those who don't have a copy of N1494:

6.11.6 Function declarators

The use of function declarators with empty parentheses (not
prototype-format parameter type declarators) is an obsolescent
feature.

I don't see much resemblance between that and incomplete type.
The obsolecence itself is irrelevant; until they're removed from
the language (and when is the committee finally going to get around
to that?), they're a required language feature.

A function type declared without a prototype is fully usable; you can
declare a pointer to it and use such a pointer in a function call.
I suppose it's "incomplete" in the sense that some information is
missing, but it's not the same kind of incompleteness that applies
to void.
 
S

Shao Miller

Shao Miller said:
jacob navia wrote:
[...]
A C type can be either a function type, an incomplete type or an object
type.

Recently the Committee agreed with Clive Feather that completeness is a
scoped property of object types (there are no incomplete function types,
after all), and the latest C1X draft says the following under 6.2.5
("Types"):

# Types are partitioned into object types (types that describe objects)
# and function types (types that describe functions). At various points
# within a translation unit an object type may be incomplete (lacking
# sufficient information to determine the size of objects of that type)
# or complete (having sufficient information).

I, too, believe it is a clearer way of describing how the C type system
works. (For Clive's rationale, see the WG14 document N1439).

Though I think the obsolescence under the N1494's 6.11.6p1 is
intuitively akin to an "incomplete function type," though such a term is
not defined in a standard sense, perhaps.

For those who don't have a copy of N1494:

6.11.6 Function declarators

The use of function declarators with empty parentheses (not
prototype-format parameter type declarators) is an obsolescent
feature.

In the future, I ought to include the point, given similar brevity. Thanks.
I don't see much resemblance between that and incomplete type.
The obsolecence itself is irrelevant; until they're removed from
the language (and when is the committee finally going to get around
to that?), they're a required language feature.
Heheh.


A function type declared without a prototype is fully usable; you can
declare a pointer to it and use such a pointer in a function call.
I suppose it's "incomplete" in the sense that some information is
missing, but it's not the same kind of incompleteness that applies
to void.

I was thinking of precisely the likeness you point out. To further
illustrate:

#define NO_WAY 0

/*** Types */
/* Incomplete */
struct s_foo;

/* Completed */
struct s_foo {
int x;
};

/* Illegal */
#if NO_WAY
struct s_foo {
int x;
double y;
};
#endif

/* Incomplete, cannot be completed */
typedef int a_bar[];

/* Complete, compatible with a_bar */
typedef int a_bar2[10];

/* Different size specifier (element count) */
typedef int a_bar3[20];

/* "Incomplete", cannot be "completed" */
typedef void f_baz();

/* "Complete" and compatible with f_baz */
typedef void f_baz2(int);

/* Different parameter specification */
typedef void f_baz3(double);

/*** Objects */
/* "Partial" */
a_bar obj_bar;

/* "Complete" */
a_bar2 obj_bar;

/* Illegal */
#if NO_WAY
a_bar3 obj_bar;
#endif

/*** Functions */
/* "Partial" */
f_baz func_baz;

/* "Complete" */
f_baz2 func_baz;

/* Illegal */
#if NO_WAY
f_baz3 func_baz;
#endif

int main(void) {
return 0;
}
 
K

Keith Thompson

jacob navia said:
Le 31/05/11 09:40, Tim Rentsch a écrit : [...]
(1) has more of a definitional flavor, but the particulars
are wrong in the context of C. Types in C are actually
fairly complicated beasts, but in essence a type is a
set of abstract values (the carrier set of the type) and
a mapping (in the mathematical sense) between the abstract
values and how those values are represented within the
computer. (Types in C also include an identity so we can
talk about two types being "the same type" or "different
types" even when the carrier sets and the mappings are the
same.) The relationship between abstract values and their
representations is better and more usually expressed as a
mapping, not by giving an algorithm.

Interesting. You would say then that int is a subset of the
natural integers from INT_MIN to INT_MAX. That would be the
mathematical definition.

Yes, in part, but that doesn't capture the identity relationship
Tim mentioned. If that were the definition, then int and long would
be the same type if INT_MIN and INT_MAX happen to equal LONG_MIN
and LONG_MAX.

I'd say that representation is also part of what a type is. Type
int isn't just a set of mathematical integers, it's also a mapping
between certain bit patterns and those mathematical integers

This is probably getting too abstruse for an introductory tutorial.

In general, if I were trying to define a term for a C tutorial,
I'd either use the C standard's definition directly, or at least
use it as a starting point for a more beginner-friendly definition.
C's definition of "type" is in 6.2.5p1 (though, as I mentioned
before, it's not really a definition, a flaw it shares with a number
of other "definitions" in the C standard).
 
T

Tim Rentsch

jacob navia said:
Le 31/05/11 09:40, Tim Rentsch a @C3{A9}crit :
jacob navia said:
[snip]

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)

(2) is not definitional. It might be appropriate to
discuss this idea as part of how/why different types
are used, but it should not be part of defining what a
type is.

Yes, I have dropped that, replacing it with a "format",
not an algorithm. "int" would be a format (sign bit,
value, two's complement, etc)
(1) has more of a definitional flavor, but the particulars
are wrong in the context of C. Types in C are actually
fairly complicated beasts, but in essence a type is a
set of abstract values (the carrier set of the type) and
a mapping (in the mathematical sense) between the abstract
values and how those values are represented within the
computer. (Types in C also include an identity so we can
talk about two types being "the same type" or "different
types" even when the carrier sets and the mappings are the
same.) The relationship between abstract values and their
representations is better and more usually expressed as a
mapping, not by giving an algorithm.

Interesting. You would say then that int is a subset of the
natural integers from INT_MIN to INT_MAX.

Yes, plus the mapping between those integers (not natural
integers, just integers) and their representations.

That would be the mathematical definition.

It's a more formal sort of definition. It isn't "mathematical",
since there is no mathematics that goes with it.

A character string would be a sequence of integers from
CHAR_MIN to CHAR_MAX of undetermined length followed by a zero.

In fact "character string" is not a type. But a character array
is fixed-length sequence of _characters_, whose abstract values
are represented by binary-encoded integers. It's convenient to
operate on the integer representations, but the abstract values
are actually "characters" (or a sequence of characters for
character arrays). Here is another reason why 'char' is a
distinct type from both 'signed char' and 'unsigned char' --
the abstract values of 'char' are not integers but characters.

Floating point would be a sequence of integers too:
boolean sign
integer exponent (possibly biased)
integer mantissa

No, that's representation again. A floating-point type
is a set of real numbers (all of which are rational),
plus the formats, etc, for how each real number in the
set is represented (sign, exponent, etc).

Is that what you mean?

As explained further above, yes.

And what about
struct customer{char *name;double balance; }; ???

An ordered pair of two values that belong to the sets
of abstract values of the types of the members, plus
how an abstract ordered pair maps to a struct in memory.

Well, operations have meaning too: 2+2 has a very concrete meaning...

The key point is types don't need operations for their
definitions.

IF we assume a mathematical description of a type.

It doesn't have to do with whether the description is
"mathematical" or not. It's because in C types are not
defined by operations.

It can be
done for some primitive types but not with user defined types.

User-defined types (that is, structs and unions) actually
are quite easy to define without including or mentioning
operations.

The difficult case is pointers, because it's hard to understand
what the abstract values for a pointer type mean without
reference to what goes on in an execution environment. Furthmore
there is an aspect of type that doesn't show up in C's type
system related to lvalue-ness. For example, if we have 'int i;',
then 'i' and '43' are both of type 'int', but '&i' results in a
pointer to int, whereas '&43' is just gibberish.


In any case thanks for your answer, it is an interesting viewpoint.

You're welcome, I'm glad it was helpful.
 
D

David Thompson

Type: int

for (result=0,counter=0; counter<CHAR_BIT*sizeof(int);counter++) {
if (int&1)
result += (1 << counter);
}

ValueOfInt = result;
int&1 is wrong unless you also shift int rightwards. You need
something like if( bits_of_int[counter] ) result += 1<<counter.

But I think writing this in C syntax is confusing; I would use either
mathematical notation (sigma for i=0 to nbits-1 of bit x 2^i)
or a prose description. 1<<n is actually 2 up n, but that
is likely not clear to people reading this kind of tutorial.
*After* you understand how binary (integer) representations work,
*then* it makes sense that 1<<n is the n'th power of two.

In fact if I change the above to the most straightforward (to me)
correct C code I get:
for( value=0, bit=0; bit<CHAR_BIT*size; bit++)
if( representation & (1<<bit) ) value += 1<<bit;
return value;
which doesn't really explain much to the reader; it just says that the
integer value of some bits is the integer value of those same bits.

Finally, this is wrong for signed-int because it doesn't handle sign.
It is correct for unsigned-int, which as I said elsethread might be
the better example for a tutorial anyway.

ObPedantic: And it doesn't deal with padding bits. I wouldn't actually
explain them in a tutorial, at least not early on, because they are
rare, but I would mention the possibility.

<snip others>
 
D

David Thompson

I paid $18 for my PDF copy of the C99 standard from ansi.org; I think
it's $30 now. (You can also pay several hundred dollars for the same thing.)

As I understand the process, not *quite* the same thing. The only
difference between the >300USD ISO version and the 30USD INCITS
version is supposed to be the paragraph on the cover page which says,
in effect, "approved by (I)NCITS". To programmers and implementors
that makes no difference; to people in a contract lawsuit it might.
Once you have the PDF, you can print it if you like, but I've
never bothered. But as I said elsethread, N1256 is better, for
most purposes, than the original C9 standard, and it's free.

Concur; for free=gratis not libre. Committee docs are still copyright,
and if you tried to go into business selling it, or worse selling a
modified version and calling it standard, I bet you'd be in trouble.
But for the purpose of asserting or refuting statements of the form
'$X is standard C', which is (part of) our topic here, yes it's fine.
 
D

David Thompson

From jacob_navia reply to Seebs (sorry, I lost the exact attribution):
\subsection{What is a type?}
A first tentative, definition for what a type is, could be “a type is a
definition of an algorithm for understanding a sequence of storage
bits”. It gives the meaning of the data stored in memory. If we say
that the object a is an int, it means that the bits stored at that
location are to be understood as a natural number that is built by
consecutive additions of powers of two as specified by its bits.

First, a nit: 'definition of an algorithm' is redundant; 'algorithm'
is sufficient. For a tutorial reader, I think it would be enough to
just say 'rule' or 'set of rules'. (To a formalist or mathematician,
'algorithm' must be effectively computable where 'rules' might not be,
but the rules here are so simple this distinction is uninteresting.)

Second, more significant, to someone who knows what 'natural number'
means this is very misleading. Naturals exclude negative, while C int
includes them. To *some* mathematicians, naturals also exclude zero;
if zero is included, naturals are a reasonable mathemetical analog to
C _unsigned_ integer types. And unsigned-int might be a better
tutorial example, because you don't have to explain the asymmetric
behavior of two's-complement, much less the different behavior of
ones'-complement and sign&magnitude.

Third, a nit: the additions here are mathematical ones and behave
properly, so order doesn't matter and 'consecutive' adds nothing.

If we say that the type of a is a double, it means that the bits are to
be understood as the IEEE 754 (for instance) standard sequences of bits
representing a double precision floating point value.
Yes. And some-integer versus some-floating is the easiest example to
explain for C. In some other languages, characters are a completely
different type than integers, and in some so are enumerations. But in
C these are just variants or as you say 'refinements' of integers, so
using them to explain types would be more difficult and less clear.

In addition to definining the values of representations (and the
representations of values), types also define the _operations_ on
those values. In particular, addition and multiplication of unsigned
int are (and must be) quite different from those for double. Addition
of pointer and integer is somewhat different, but less dramatically.
A second, more refined definition would encompass the first but add the
notion of "concept" behind a type. For instance in some machines
the type "size_t" has exactly the same bits as an unsigned long, yet,
it is a different type. The difference is that we store sizes in
size_t objects, and not some arbitrary integer. The type is associated
with the concept of size. We use types to convey a concept to the
reader of the program.
The sizes of objects, or in simple terms of memory areas. And often
also offsets or subscripts within those obects, although you may not
want to go into that here. But not the sizes of, for examples, a box
to be shipped by post, or a floor to be covered with tiles.

I sometimes talk about this as 'purpose' rather than 'concept'.
I think that's slightly more specific, although not much.
The base of C's type hierarchy are the machine types, i.e. the types
that the integrated circuit understands. C has abstracted from the
myriad of machine types some types like 'int' or 'double' that are
almost universally present in all processors.

There are many machine types that C doesn't natively support, for
instance some processors support BCD coded data but that data is
accessible only through special libraries in C.
I'm not sure about 'many', at least in comparison to those supported,
but certainly some. BCD is a good example. Actually it needs either
library(ies) or extension(s) -- I've used one compiler that did a
core-language extension. But either way it's not standard C.

<snip rest>
 
M

Marcin Grzegorczyk

jacob said:
Le 30/05/11 22:42, Marcin Grzegorczyk a écrit :
jacob said:
[...]
A C type can be either a function type, an incomplete type or an object
type.

Recently the Committee agreed with Clive Feather that completeness is a
scoped property of object types (there are no incomplete function types,
after all), and the latest C1X draft says the following under 6.2.5
("Types"): [quotation snipped]
I, too, believe it is a clearer way of describing how the C type system
works. (For Clive's rationale, see the WG14 document N1439).

After reading N1439 I would tend to agree with what it says but
where should go the void type?

It is void an object type?

Yes, void belongs to object types, even if it might seem weird at first.

Consider how the void type is used:
* As a function return type. Function return types are object types.
* As a function parameter type (with special constraints). Function
parameter types are object types, too.
* As a pointed-to type. There are pointers to object types and pointers
to function types; `void*` is compatible only with the former.

[snip]
The problem is that N1429 is difficult to read because it only
refers to what should be patched and doesn't display the patched
text.

I brought N1429 only for the rationale. (The latest C1X draft is N1569
/ N1570 (without / with diff marks, respectively).)
 
J

jacob navia

Le 02/06/11 22:44, Marcin Grzegorczyk a écrit :
Yes, void belongs to object types, even if it might seem weird at first.

Well, YES...

It seems weird to me, I wonder how the readers of the tutorial will
react.

In any case I will update the tutorial to reflect the current
thinking of the committee.
Consider how the void type is used:
* As a function return type. Function return types are object types.
* As a function parameter type (with special constraints). Function
parameter types are object types, too.
* As a pointed-to type. There are pointers to object types and pointers
to function types; `void*` is compatible only with the former.

[snip]
The problem is that N1429 is difficult to read because it only
refers to what should be patched and doesn't display the patched
text.

I brought N1429 only for the rationale. (The latest C1X draft is N1569 /
N1570 (without / with diff marks, respectively).)

OK. Thanks.

jacob
 
K

Keith Thompson

jacob navia said:
Le 02/06/11 22:44, Marcin Grzegorczyk a écrit :

Well, YES...

It seems weird to me, I wonder how the readers of the tutorial will
react.

In any case I will update the tutorial to reflect the current
thinking of the committee.
[...]

That's up to you, of course, but in the current standard void is
not an object type. It's an incomplete type, and incomplete types
are disjoint from object types.

C201X has no official standing yet, and there are no conforming
implementations.

Are you writing a C99 tutorial or a C201X tutorial? If the latter,
I suggest making it clear that you're referring to a future version
of the language.

Or you might consider writing both, using something similar to
#ifdef to let you generate either version (depending on what tool
you're using to write and publish the tutorial).

(In this particular case, it's just a different way of describing the
same thing.)
 

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