[META] Talking about talking about C.

Underneath it all, it is perhaps useful to remember that most of the
active participants here are people who have successfully written reasonably
large programs in C, so however they're thinking about C inside their heads,
it *works*. Maybe it's not exactly correct, but at the bare minimum, you
can be reasonably confident that it's a good enough model to have both
explanatory and predictive power for the behavior of programs on real
machines.

One of the most useful non-engineering skills I've ever applied to engineering
is learning to communicate better. Interestingly, writing clearly, while
certainly useful, is by far the lesser part of this. The big thing is to
learn to listen better. And I still have a long way to go on that.

A friend of mine gave me an excellent summary of a useful tactic when someone
who is otherwise apparently pretty rational or well-informed says something
obviously false:
Rather than thinking of it as false, think of it as true, and try
to figure out what it could be true *of*.

That's certainly contrary to the way engineers usually think! However, it's
very useful.

Here's an example of how I might apply that. It is obvious to me that size_t
is distinct from any of the standard unsigned integer types. Someone comes
along and tells me that size_t is the same as one of those types. Obviously
this is untrue... But wait! He's no idiot. What's he talking about?

Well, it turns out, what he's saying is quite true if, instead of talking
about the abstract language spec, I talk about any real implementation I've
ever heard of. On any real implementation I've seen, size_t is the same
type as one of unsigned short, unsigned int, unsigned long, or unsigned long
long. I've never heard of an exception. So actually, he's saying something
true, but he's talking about something other than what I'm talking about. If
I understand this, I can get somewhere. If I don't, I'm going to spend a
lot of time telling him that he's wrong, which he's not going to believe,
because he's tested his claim and it's obviously true.

Once we've got that figured out, we can potentially have a productive
discussion about whether it's more useful or effective to think about types
in terms of logical types or in terms of the compatibility rules that
are invoked when you try to assign a 'foo *' to a 'bar *'. Both ways of
thinking about types work. You can write large programs thinking about
it either way. Furthermore, each way of thinking about types will expose
you to some problems -- which the other avoids.

The same thing comes up when, say, someone talks about "the stack". All
the experienced C programmers know that C doesn't have a stack, except the
ones who know that C does have a stack. There are at least three reasonably
plausible ways of thinking about this. You can treat it as a contiguous
block of memory which always grows in the same direction. Sure, it's not
exactly right, but it'll work well enough most of the time, and it's simple.
You can treat it as totally separate activation records. You can think of
it more like a LIFO queue, without any regard to how it goes together.

All of these models *work*. None of them are exactly mandated. If, instead
of calling people "wrong" when they use a different model, you try to
understand the model they're using and see how it could be useful to you,
you're more likely to learn something.

Learning to distinguish between the thing you're modeling, and your model
of it, is really useful. It can make you a better programmer. It can
also reduce, dramatically, the number of clueless jerks you encounter on
Usenet.

Advertisements

No, everyone sucks at communicating.
> A friend of mine gave me an excellent summary of a useful tactic when someone
> who is otherwise apparently pretty rational or well-informed says something
> obviously false:
> Rather than thinking of it as false, think of it as true, and try
> to figure out what it could be true *of*.
>

Much too absolute. If someone says that all crows are white, it is
false. It's the height of impertinence to nod patronisingly while
deciding inwardly that they're actually talking about polar bears.
> Here's an example of how I might apply that. It is obvious to me that size_t
> is distinct from any of the standard unsigned integer types.

No, no! It's distinct from *all but one* of the standard unsigned
integer types.
> All of these models *work*. None of them are exactly mandated. If, instead
> of calling people "wrong" when they use a different model, you try to
> understand the model they're using and see how it could be useful to you,
> you're more likely to learn something.
>

People will no more agree that they are arguing about different
"models" than they will agree that they are arguing about different
"subjectivities".

Advertisements

Seebs <> writes:
> So, watching the typedef battles, something has become clear to me.
>
> Engineers suck at communicating.
>
> Underneath it all, it is perhaps useful to remember that most of the
> active participants here are people who have successfully written reasonably
> large programs in C, so however they're thinking about C inside their heads,
> it *works*. Maybe it's not exactly correct, but at the bare minimum, you
> can be reasonably confident that it's a good enough model to have both
> explanatory and predictive power for the behavior of programs on real
> machines.
>
> One of the most useful non-engineering skills I've ever applied to engineering
> is learning to communicate better. Interestingly, writing clearly, while
> certainly useful, is by far the lesser part of this. The big thing is to
> learn to listen better. And I still have a long way to go on that.
>
> A friend of mine gave me an excellent summary of a useful tactic when someone
> who is otherwise apparently pretty rational or well-informed says something
> obviously false:
> Rather than thinking of it as false, think of it as true, and try
> to figure out what it could be true *of*.
>
> That's certainly contrary to the way engineers usually think! However, it's
> very useful.

Well said. I probably haven't worked hard enough at this myself in the
typedef thread.

Of course I can't resist the urge to quibble with some of the details
even in this post. }
> Here's an example of how I might apply that. It is obvious to me that size_t
> is distinct from any of the standard unsigned integer types. Someone comes
> along and tells me that size_t is the same as one of those types. Obviously
> this is untrue... But wait! He's no idiot. What's he talking about?
>
> Well, it turns out, what he's saying is quite true if, instead of talking
> about the abstract language spec, I talk about any real implementation I've
> ever heard of. On any real implementation I've seen, size_t is the same
> type as one of unsigned short, unsigned int, unsigned long, or unsigned long
> long. I've never heard of an exception. So actually, he's saying something
> true, but he's talking about something other than what I'm talking about. If
> I understand this, I can get somewhere. If I don't, I'm going to spend a
> lot of time telling him that he's wrong, which he's not going to believe,
> because he's tested his claim and it's obviously true.

You raise a point that I had mostly ignored: size_t could be a typedef
for something other than one of the five predefined unsigned integer
types (you forgot unsigned char). In particular, it could be a typedef
for one of the extended unsigned integer types. (C99 only; C90 didn't
have extended integer types.)

(Note that the description of the 'z' modifier for fprintf implicitly
assumes that a size_t argument is not promoted, and therefore that
size_t is not unsigned short or unsigned char. Note also that a
strict reading implies that size_t cannot be a typedef for plain
char, even if char happens to be unsigned, because char is not
one of the "unsigned integer types", even if it's an integer type
that's unsigned. I'd be astonished if either of these mattered
for any real-world implementation.)

So saying that size_t is the same as one of the standard unsigned
integer types is imprecise. But saying that size_t is the same as one
of the "unsigned integer types" (as that term is defined in C99
6.2.5p6) is both precise and correct.

C99 6.7.7p3:
A typedef declaration does not introduce a new type, only a synonym
for the type so specified.
That's about as clear as it can be.

The title of 6.7.7 is "Type definitions". The introductory subsections
for several standard headers refer to types (such as size_t) being
"defined". The only consistent conclusion is that "defining" a type
does not "introduce" (or create) a new type.

Thus my position, that size_t is not a distinct type, but is the same
type as one of the unsigned integer types. And this is on the level of
the abstract language spec, *not* with respect to any particular
implementation. The choice of which unsigned integer type size_t is the
same type as is implementation-specific. The fact that it's the same as
one of them is not.
> Once we've got that figured out, we can potentially have a productive
> discussion about whether it's more useful or effective to think about types
> in terms of logical types or in terms of the compatibility rules that
> are invoked when you try to assign a 'foo *' to a 'bar *'. Both ways of
> thinking about types work. You can write large programs thinking about
> it either way. Furthermore, each way of thinking about types will expose
> you to some problems -- which the other avoids.

I understand that you think of size_t as a distinct type. I agree 100%
that it's useful to think of size_t as a distinct *something*. The
point where I disagree is the use of the word "type" to describe it.

Yes, if I program *as if* size_t were an additional predefined
integer type, distinct from all others, that's likely to lead me
to write good and/or portable code. (Similarly, I tend to pretend
that compound statements require braces, even though of course
they don't.) And the "size_t is a distinct type" model is simpler
than the "size_t is the same as one of the unsigned integer types,
but which one it is depends on the implementation, and writing code
as if it were a distinct type is likely to lead to better results"
model. It's just that the latter model is correct -- assuming
that I use the word "type" the same way the standard does.
> The same thing comes up when, say, someone talks about "the stack". All
> the experienced C programmers know that C doesn't have a stack, except the
> ones who know that C does have a stack. There are at least three reasonably
> plausible ways of thinking about this. You can treat it as a contiguous
> block of memory which always grows in the same direction. Sure, it's not
> exactly right, but it'll work well enough most of the time, and it's simple.
> You can treat it as totally separate activation records. You can think of
> it more like a LIFO queue, without any regard to how it goes together.
>
> All of these models *work*. None of them are exactly mandated. If, instead
> of calling people "wrong" when they use a different model, you try to
> understand the model they're using and see how it could be useful to you,
> you're more likely to learn something.

On the other hand, assuming the contiguous block of memory model can
lead to errors. For example, you might assume that comparisons of
addresses between different function calls (perhaps logged with
fprintf(log_file, "%p", ...)) tells you something about the calling
sequence. (And on most implementations, it does.)

On the other other hand, it's certainly possible to have a mental
model of "the stack" as a contiguous block of memory that always
grows in a consistent direction and still write good and portable
code. But it requires avoiding depending on some of the implications
of that model. Personally, I prefer to use a mental model that,
though it might be more complex and/or abstract, doesn't make me
keep track of which implications I can actually depend on and which
I can't. In addition, the rule that you can't compare pointers to
distinct objects with <, <=, >, or >= seems a lot less arbitrary.

(And as usual, that was a lot longer than I thought it would be.)
> Learning to distinguish between the thing you're modeling, and your model
> of it, is really useful. It can make you a better programmer. It can
> also reduce, dramatically, the number of clueless jerks you encounter on
> Usenet.
>
> -s

--
Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

On 2010-10-23, Keith Thompson <> wrote:
> Of course I can't resist the urge to quibble with some of the details
> even in this post. }

Of course!
> You raise a point that I had mostly ignored: size_t could be a typedef
> for something other than one of the five predefined unsigned integer
> types (you forgot unsigned char). In particular, it could be a typedef
> for one of the extended unsigned integer types. (C99 only; C90 didn't
> have extended integer types.)

Right.
> So saying that size_t is the same as one of the standard unsigned
> integer types is imprecise. But saying that size_t is the same as one
> of the "unsigned integer types" (as that term is defined in C99
> 6.2.5p6) is both precise and correct.

Yes.

However, I'm not sure it's *useful* -- so far as I can tell, any program
which is correct only if that is true is logically incorrect.
> C99 6.7.7p3:
> A typedef declaration does not introduce a new type, only a synonym
> for the type so specified.
> That's about as clear as it can be.

Yes. I think the problem is that the word "type" is used in two different
ways in the standard; sometimes to refer to the underlying thing that has
to be compatible, sometimes to the relation between a name and that thing.
> The title of 6.7.7 is "Type definitions". The introductory subsections
> for several standard headers refer to types (such as size_t) being
> "defined". The only consistent conclusion is that "defining" a type
> does not "introduce" (or create) a new type.

I'm inclined to agree.
> Thus my position, that size_t is not a distinct type, but is the same
> type as one of the unsigned integer types. And this is on the level of
> the abstract language spec, *not* with respect to any particular
> implementation. The choice of which unsigned integer type size_t is the
> same type as is implementation-specific. The fact that it's the same as
> one of them is not.

Yup. But there's no specific type it's necessarily the same as, and it's
possible (though unheard of) for an implementation to have no other visible
type which is the same as it.

Huh. Actually, that leads to an interesting question.

We already know that "long" and "int" can be distinct types even when they
have the same representation.

Imagine, if you will, an implementation which creates a new type:
__SIZE_T
which is a 32-bit unsigned integer type which is distinct from unsigned long,
but which is an extended unsigned integer type.

So far as I can tell, it's fine for size_t to then be a typedef for __SIZE_T,
which is incompatible with EVERY other type.
> I understand that you think of size_t as a distinct type. I agree 100%
> that it's useful to think of size_t as a distinct *something*. The
> point where I disagree is the use of the word "type" to describe it.

I think it'd be a lot easier to work on that if the language had better
language for the distinction between the things which are defined by
<stddef.h> and the things which are created when you create a new struct
type.
> Yes, if I program *as if* size_t were an additional predefined
> integer type, distinct from all others, that's likely to lead me
> to write good and/or portable code. (Similarly, I tend to pretend
> that compound statements require braces, even though of course
> they don't.) And the "size_t is a distinct type" model is simpler
> than the "size_t is the same as one of the unsigned integer types,
> but which one it is depends on the implementation, and writing code
> as if it were a distinct type is likely to lead to better results"
> model. It's just that the latter model is correct -- assuming
> that I use the word "type" the same way the standard does.

Well, the same way the standard does sometimes. I think mine is correct
if you use "type" the way it's used in the description of <stddef.h>.
And that's really the problem -- we seem to have two quite clearly
incompatible usages. I suppose in theory this is a defect. It's mostly
a harmless one, though -- this is the first time I've ever seen it come
up.

More interestingly, so far as I can tell, there's not a single case on the
table of either way of talking about types creating a problem for an actual
user thinking about or writing in C. They're incompatible with each others'
premises and terminology, but each seems to be internally consistent and
consistent with at least some of the usage in the standard.
> (And as usual, that was a lot longer than I thought it would be.)

On 2010-10-23, MartinBroadhurst <> wrote:
> On 23 Oct, 19:44, Seebs <> wrote:
>> Here's an example of how I might apply that. ?It is obvious to me that size_t
>> is distinct from any of the standard unsigned integer types.
> No, no! It's distinct from *all but one* of the standard unsigned
> integer types.

It could be distinct from all of them -- it could be an extended unsigned
integer type.

However, I think you're missing the point of what I'm saying. Consider
that I almost certainly understand how typedef works and so on -- I spend
a decade on the ISO C committee. You might, then, reasonably conclude
that, especially after spending a couple of days arguing about this very
point and citing the standard, I *probably* actually understand it.

So what on earth is happening when I make this statement which appears to
be false? Presumably, I must be trying to express something different.

And that is this: Even if we know, for sure, that some header somewhere
says:
typedef unsigned long size_t;
it is still possible, and useful, to distinguish between "size_t" and
"unsigned long". It is better to write:
printf("%zu\n", sizeof(x));
than
printf("%lu\n", sizeof(x));

Why? Because, even though it is true that size_t is almost certainly*
defined in terms of some other type, it is *logically* distinct, and treating
it as though it were completely distinct will almost inevitably produce
better code than treating it as though it were not completely distinct.

-s
[*] Actually, on thinking about it, I am not sure this is the case. So
far as I can tell, an implementation is permitted to have size_t itself
*be* the extended unsigned integer type, at which point, it's not an alias
for anything. I certainly can't conceive of a strictly conforming
program which could show such an implementation to be in error.
--
Copyright 2010, all wrongs reversed. Peter Seebach / http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictureshttp://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
I am not speaking for my employer, although they do rent some of my opinions.

On 2010-10-24, pete <> wrote:
> Seebs wrote:
>> On 2010-10-23, MartinBroadhurst <> wrote:
>>> On 23 Oct, 19:44, Seebs <> wrote:
>>>> Here's an example of how I might apply that. ?It is obvious to me that size_t
>>>> is distinct from any of the standard unsigned integer types.
>>> No, no! It's distinct from *all but one* of the standard unsigned
>>> integer types.
>> It could be distinct from all of them -- it could be an extended unsigned
>> integer type.
> You just made that up.

Nope!
> The standard says that size_t is
> "the unsigned integer type of the result of the sizeof operator".

See 6.2.5 (Types), paragraphs 4 and 6. The standard allows you to have
more integer types than are denoted by the usual range from unsigned char
to unsigned long long. While this doesn't come up very often, it might
make sense for an implementation to, say, use the common 8/16/32/64 types
for the standard unsigned integer types, then also provide at least one
extended unsigned integer type, such as a 24-bit or 48-bit type.

An implementor who hated you could in theory use 64 for all the standard
unsigned integer types except unsigned char, and then have the 16-bit
and 32-bit types be extended unsigned integer types.

Re-reading it, though, it seems that the extended types have to use
identifiers reserved for any use (footnote 28). So it seems to me that
there must exist some name for that type which is not size_t, so
size_t has to be an alias for some type, though there may be no way
for standard code to use that type directly.

Seebs <> writes:
[...]
> [*] Actually, on thinking about it, I am not sure this is the case. So
> far as I can tell, an implementation is permitted to have size_t itself
> *be* the extended unsigned integer type, at which point, it's not an alias
> for anything. I certainly can't conceive of a strictly conforming
> program which could show such an implementation to be in error.

If size_t is a typedef for an extended unsigned integer type,
that type has a name which is an implementation-defined keyword
(or perhaps a sequence of keywords like "unsigned __int32").

If size_t itself were an extended unsigned integer type, then size_t
would be a keyword.

A strictly conforming program can use "size_t" as an identifier,
for example as the name of an automatic variable.

--
Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Seebs <> writes:
[...]
> Re-reading it, though, it seems that the extended types have to use
> identifiers reserved for any use (footnote 28). So it seems to me that
> there must exist some name for that type which is not size_t, so
> size_t has to be an alias for some type, though there may be no way
> for standard code to use that type directly.

A conforming program can use the type name directly. (A strictly
conforming, or even a portable or "clc-compliant" program, cannot.)

--
Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

On 2010-10-24, Keith Thompson <> wrote:
> Seebs <> writes:
>> However, I'm not sure it's *useful* -- so far as I can tell, any program
>> which is correct only if that is true is logically incorrect.
> Such a program can be non-portable, but it's not necessarily logically
> incorrect.

Interesting point!
> The initialization, which is non-portable, might be intended as a
> compile-time assertion that size_t is a typedef for unsigned long.
> Maybe I have a reason to want my program to compile only if that
> assumption is correct. Maybe it reduces the amount of testing I
> have to do. Or maybe I just want to see how it compiles as a way
> of learning more about the implementation I'm using -- which is a
> perfectly valid goal.

As a compile-time assertion, that's sort of clever.
>>> C99 6.7.7p3:
>>> A typedef declaration does not introduce a new type, only a synonym
>>> for the type so specified.
>>> That's about as clear as it can be.
>> Yes. I think the problem is that the word "type" is used in two different
>> ways in the standard; sometimes to refer to the underlying thing that has
>> to be compatible, sometimes to the relation between a name and that thing.
> Hmmm. I don't think I agree.
> If a "type" is sometimes, as you say, "the relation between a name and
> that thing", then in that sense the declarations in <stddef.h> *create*
> types. But we know that typedefs *don't* create types, because 6.7.7p3
> says they don't.

Ahh, but the key here is *sometimes*. My assertion is that 6.7.7 is using
the word "type" in the sense of "underlying thing", and that the <stddef.h>
section is using the word "type" in the sense of "relation between name and
thing".

Consider that, when you're looking at <stddef.h>, it's irrelevant to you
whether size_t is the same as any other type or not -- you are only concerned
with the fact that you can declare things of the type size_t after you
have included the header. But when you're reading 6.7.7, you're looking
into the actual underlying question of how typedef works. In that context,
you *do* need to know about that distinction -- that it is just creating
a new name for an existing thing.
> I suspect (of course, correct me if I'm wrong) your reasoning was
> something like this:
> 1. <stddef.h> defines types.
> 2. Defining types means creating types.

I'm not sure about this. The more I think about it, the more I think that
"creating" is the wrong interpretation of "defining", and that a whole lot
of the disputes seem to come from the assertion that to define something is
to create it.
> If so, I think step 2 is where you went astray.

I think it is, at any event, a claim that I'm currently inclined to
reject.
>> Yup. But there's no specific type it's necessarily the same as, and it's
>> possible (though unheard of) for an implementation to have no other visible
>> type which is the same as it.
> I'm not sure it is possible. size_t must be an "unsigned integer
> type" (7.17p2). The "unsigned integer types" are the five "standard
> unsigned integer types" plus the "extended unsigned integer types"
> (of which there are zero or more). 6.2.5 doesn't actually say that
> each of the extended unsigned integer types has a name, but I think
> it assumes that it does; a footnote on the sentence that introduces
> extended integer types discusses the form of implementation-defined
> keywords.

Yes.

Thus the qualifier "visible". If the implementation used __SIZE_T_MAGIC
as the type that size_t is an alias for, there's no way for you to "see"
that. You can't interact with it, you can't use the name, you can't
do anything with it. The only *visible* type is size_t.
>> So far as I can tell, it's fine for size_t to then be a typedef for __SIZE_T,
>> which is incompatible with EVERY other type.
> Yes. It's also fine for size_t to be a typedef for unsigned long,
> which is incompatible with every other type. I don't see that the
> __SIZE_T case is significantly different.

The difference is that __SIZE_T is reserved for *all* uses. The
implementation is free to prohibit you from declaring anything using
that spelling, so far as I can tell.
>> I think it'd be a lot easier to work on that if the language had better
>> language for the distinction between the things which are defined by
>> <stddef.h> and the things which are created when you create a new struct
>> type.
> I think it does; they're called typedefs and structs types,
> respectively.

Yeah, "typedef names" is actually pretty useful.

C would benefit from a way to tag whether the intent is to make an
incompatible new type or just to have an easier way to spell a type
without any incompatibilities.

On 2010-10-24, Keith Thompson <> wrote:
> If size_t is a typedef for an extended unsigned integer type,
> that type has a name which is an implementation-defined keyword
> (or perhaps a sequence of keywords like "unsigned __int32").
>
> If size_t itself were an extended unsigned integer type, then size_t
> would be a keyword.
>
> A strictly conforming program can use "size_t" as an identifier,
> for example as the name of an automatic variable.

Your argument is persuasive, as is the observation that the extended
types are required to have names that are reserved for any use by
the implementation.

On 2010-10-24, Keith Thompson <> wrote:
> Seebs <> writes:
> [...]
>> Re-reading it, though, it seems that the extended types have to use
>> identifiers reserved for any use (footnote 28). So it seems to me that
>> there must exist some name for that type which is not size_t, so
>> size_t has to be an alias for some type, though there may be no way
>> for standard code to use that type directly.
> A conforming program can use the type name directly. (A strictly
> conforming, or even a portable or "clc-compliant" program, cannot.)

I am toying with a notion: Imagine a compiler which has extended types
which you are NOT allowed to use -- any attempt to declare an object of
these types gets you compiler errors. And which then has typedef "bless"
them, so a typedef name pointing at them can be used.

So far as I can tell, "reserved for any use" would permit this kind of
silliness.

Seebs <> writes:
> On 2010-10-24, Keith Thompson <> wrote:
[...]
>> If a "type" is sometimes, as you say, "the relation between a name and
>> that thing", then in that sense the declarations in <stddef.h> *create*
>> types. But we know that typedefs *don't* create types, because 6.7.7p3
>> says they don't.
>
> Ahh, but the key here is *sometimes*. My assertion is that 6.7.7 is using
> the word "type" in the sense of "underlying thing", and that the <stddef.h>
> section is using the word "type" in the sense of "relation between name and
> thing".
>
> Consider that, when you're looking at <stddef.h>, it's irrelevant to you
> whether size_t is the same as any other type or not -- you are only concerned
> with the fact that you can declare things of the type size_t after you
> have included the header.

Perhaps, depending on just why I'm looking at it.
> But when you're reading 6.7.7, you're looking
> into the actual underlying question of how typedef works. In that context,
> you *do* need to know about that distinction -- that it is just creating
> a new name for an existing thing.
>
>> I suspect (of course, correct me if I'm wrong) your reasoning was
>> something like this:
>
>> 1. <stddef.h> defines types.
>> 2. Defining types means creating types.
>
> I'm not sure about this. The more I think about it, the more I think that
> "creating" is the wrong interpretation of "defining", and that a whole lot
> of the disputes seem to come from the assertion that to define something is
> to create it.
>
>> If so, I think step 2 is where you went astray.
>
> I think it is, at any event, a claim that I'm currently inclined to
> reject.

Cool.

Consider also something I mentioned in another thread. Look at the
definition of "definition" in 6.7p5. It's restricted to object
definitions, function definitions, enumeration constant (*not*
enumeration types), and typedef names. Other constructs that
create new types, such as struct declarations are not "definitions".
So defining something doesn't mean creating something. (Later I'll
try to figure out just what the standard really means by "defining"
something).

[...]
>>> So far as I can tell, it's fine for size_t to then be a typedef for __SIZE_T,
>>> which is incompatible with EVERY other type.
>
>> Yes. It's also fine for size_t to be a typedef for unsigned long,
>> which is incompatible with every other type. I don't see that the
>> __SIZE_T case is significantly different.
>
> The difference is that __SIZE_T is reserved for *all* uses. The
> implementation is free to prohibit you from declaring anything using
> that spelling, so far as I can tell.

Hmm.

In this hypothetical implementation, the implementation chose
to reserve the name __SIZE_T as a keyword that's the name
for an extended integer type. Once it's done that, I'd expect
(non-portable) programs to be able to use that keyword to refer to
that same type.

I suppose the implementation could arbitrarily restrict the use
of that keyword other than in standard headers; I'm not quite sure
whether it would be legal for it to do so.

And I think we're off on a tangent.
>>> I think it'd be a lot easier to work on that if the language had better
>>> language for the distinction between the things which are defined by
>>> <stddef.h> and the things which are created when you create a new struct
>>> type.
>
>> I think it does; they're called typedefs and structs types,
>> respectively.
>
> Yeah, "typedef names" is actually pretty useful.
>
> C would benefit from a way to tag whether the intent is to make an
> incompatible new type or just to have an easier way to spell a type
> without any incompatibilities.

Yeah, I think I may have mentioned that a few dozen followups ago. }

--
Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

On 2010-10-24, Keith Thompson <> wrote:
> Seebs <> writes:
>> Consider that, when you're looking at <stddef.h>, it's irrelevant to you
>> whether size_t is the same as any other type or not -- you are only concerned
>> with the fact that you can declare things of the type size_t after you
>> have included the header.
> Perhaps, depending on just why I'm looking at it.

I can't immediately think of a purpose for which I'd want to use the
information that size_t is probably an alias for a standard integer
type, except maybe for debugging a program which might be making
a mistake in relying on that in some way.
> Consider also something I mentioned in another thread. Look at the
> definition of "definition" in 6.7p5. It's restricted to object
> definitions, function definitions, enumeration constant (*not*
> enumeration types), and typedef names. Other constructs that
> create new types, such as struct declarations are not "definitions".
> So defining something doesn't mean creating something. (Later I'll
> try to figure out just what the standard really means by "defining"
> something).

My guess:

For things you generate code for, it's the thing that causes the
code to be generated.

For things that are names or words, it's the thing that gives
meaning to the name or word.
> In this hypothetical implementation, the implementation chose
> to reserve the name __SIZE_T as a keyword that's the name
> for an extended integer type. Once it's done that, I'd expect
> (non-portable) programs to be able to use that keyword to refer to
> that same type.
> I suppose the implementation could arbitrarily restrict the use
> of that keyword other than in standard headers; I'm not quite sure
> whether it would be legal for it to do so.

On Sat, 2010-10-23, Keith Thompson wrote:
> Seebs <> writes:
....
>> The same thing comes up when, say, someone talks about "the stack". All
>> the experienced C programmers know that C doesn't have a stack, except the
>> ones who know that C does have a stack. There are at least three reasonably
>> plausible ways of thinking about this. You can treat it as a contiguous
>> block of memory which always grows in the same direction. Sure, it's not
>> exactly right, but it'll work well enough most of the time, and it's simple.
>> You can treat it as totally separate activation records. You can think of
>> it more like a LIFO queue, without any regard to how it goes together.
....
> On the other hand, assuming the contiguous block of memory model can
> lead to errors. For example, you might assume that comparisons of
> addresses between different function calls (perhaps logged with
> fprintf(log_file, "%p", ...)) tells you something about the calling
> sequence. (And on most implementations, it does.)
>
> On the other other hand, it's certainly possible to have a mental
> model of "the stack" as a contiguous block of memory that always
> grows in a consistent direction and still write good and portable
> code. But it requires avoiding depending on some of the implications
> of that model. Personally, I prefer to use a mental model that,
> though it might be more complex and/or abstract, doesn't make me
> keep track of which implications I can actually depend on and which
> I can't.

My mental model is approximately the Motorola 68000, with an actual
stack pointer register and everything. But I have never had problems
grasping that it might look very different ... and invalid code
doesn't look more right to me just because it fits my MC68000 model;

unsigned f(unsigned char* p) { return *(unsigned*)p; }

just looks /wrong/ (to take an example unrelated to stacks).

I don't think it's just me and my superior mind ;-) -- I think the
perils of very concrete mental models are exaggerated.

superpollo <> writes:
> Keith Thompson ha scritto:
[...]
> what about this?
>
> http://cprog.tomsweb.net/cintro.html#2.4
>
> "The type char may be equivalent to either signed char or unsigned char
> (that depends on your compiler), but it is always a separate type from
> either of these."
>
> so what does equivalent mean?

It means that they have the same size, representation, bounds, and so
forth.

--
Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Seebs wrote:
> An implementor who hated you could in theory use 64 for all the standard
> unsigned integer types except unsigned char, and then have the 16-bit
> and 32-bit types be extended unsigned integer types.

Keith Thompson wrote:
[...]
> Yes, if I program *as if* size_t were an additional predefined
> integer type, distinct from all others, that's likely to lead me
> to write good and/or portable code. (Similarly, I tend to pretend
> that compound statements require braces, even though of course
> they don't.)

You've got me lost here. Braces are in the syntax of a compound
statement (6.8.2).
--
Marcin Grzegorczyk

On 2010-10-24, Marcin Grzegorczyk <> wrote:
> Keith Thompson wrote:
> [...]
>> Yes, if I program *as if* size_t were an additional predefined
>> integer type, distinct from all others, that's likely to lead me
>> to write good and/or portable code. (Similarly, I tend to pretend
>> that compound statements require braces, even though of course
>> they don't.)
> You've got me lost here. Braces are in the syntax of a compound
> statement (6.8.2).

I think what Keith is referring to is "the body of a control statement".

e.g.:
if (foo) {
bar;
}

So really, "as if the body of a control statement had to be a compound
statement".

Share This Page

Welcome to The Coding Forums!

Welcome to the Coding Forums, the place to chat about anything related to programming and coding languages.

Please join our friendly community by clicking the button below - it only takes a few seconds and is totally free. You'll be able to ask questions about coding or chat with the community and help others.
Sign up now!