Does your C compiler support "//"? (was Re: using structures)

R

Richard Tobin

Douglas A. Gwyn said:
As a long-time proponent of code portability I hate to see that
happen.

Of course there are reasons *against* portability as well. I have
used computers with features so stupid or awkward that I would rather
my code didn't work on them, in the hope that someone who needed my
program might use it as a factor when choosing between machines.

8086-type segmented pointers are an example.

-- Richard
 
K

Keith Thompson

In other words, wchar_t is *not* a true type on its own.

Yes, I believe it is; thus the phrase "distinct type".

Consider a C implementation in which int and long have the same size,
signedness, and alignment requirements (such implementations are very
common). Implicit conversions mostly mask the fact that they're
distinct types, but the following program is still illegal (and would
be legal if they weren't distinct):

int main(void)
{
int *ip = 0;
long *lp = 0;
ip = lp;
return 0;
}

I believe the same is true for wchar_t and its "underlying type" (or,
more topically, it would be if the C standard had adopted C++'s
definition of wchar_t).
There are good uses for both flavours of typedef. Having "typedef" and
"typealias" would have been the right thing.

Agreed. As it is, though, "typedef" already creates an alias, and
changing that could break too much code; if we wanted to add this in a
future standard, we'd need a different keyword that creates a distinct
type.

How about "static typedef"? :cool:}


[snip]
But if they don't match, the compiler *must* generate a diagnostic, so
where is the problem?

The problem is that it's non-portable code for which a compiler can't
easily generate a warning.

If I use a long argument with a "%d" printf format, a sufficiently
friendly compiler will warn me about it, even if int and long happen
to have the same characteristics. In the above example, it can't
reasonably issue a similar warning, since size_t and unsigned long
really are the same type on the platform in question. I suppose a
more aggressively friendly compiler could issue a warning anyway, but
there's no good way for the compiler to tell whether a given typedef
is meant to create a logically distinct type or just an alias.
The problem is caught as soon as it becomes a problem ;-)

If the code is unintentionally non-portable, I'd consider that an
immediate problem; I'd much rather fix it *before* I try to port the
code.
Adding a *new* keyword with your preferred semantics is still doable.
And changing things like size_t from their current status of type alias
to the status of "new type" shouldn't hurt existing *correct* code, as
long as the new type inherits all the properties of the underlying type.

I agree on the first point, but making size_t a distinct type could
make some existing correct (but non-portable) code illegal; I provided
an example above. I'm tempted to suggest that it's worth it.
 
P

Paul Eggert

It is really easy to avoid depending on all pointers having the same format

It is really easy to do a lot of things; but at some point all the
"really easy" requirements pile up and become make-work that gets in
the way of the real work. One has to draw the line somewhere.

Non-uniform pointers and I go a way back. The first computer I ever
used was one of them: the General Electric 225 (introduced in 1960;
the biggest model had 16,384 20-bit words). I've written a lot of C
code that is portable to them (in theory), and I've even written
assembly code for them (code that actually ran and was used by
others). I know they still exist, but I'm thankful that I no longer
have to worry about them.
 
C

CBFalconer

Richard said:
.... snip ...

Of course there are reasons *against* portability as well. I have
used computers with features so stupid or awkward that I would rather
my code didn't work on them, in the hope that someone who needed my
program might use it as a factor when choosing between machines.

8086-type segmented pointers are an example.

They still exist in your (probable) current machine. However, all
segments are set to point to the same address space, giving the
illusion of non-segmented memory. Or the segments are under the
exclusive control of the OS, again leaving an illusion.

Segments have many advantages, among which are reduction of code
size and control of memory usage. Today these may not be primary
considerations.
 
D

Dan Pop

In said:
^^^^^^^^^^^^^^^^^^
They still exist in your (probable) current machine. However, all
segments are set to point to the same address space, giving the
illusion of non-segmented memory. Or the segments are under the
exclusive control of the OS, again leaving an illusion.

You seem to be confusing memory segments and segmented pointers.
Having a segmented address space doesn't necessarily mean segmented
pointers: even on the 8086 implementations, there were memory models
that did not require such things (tiny and small).

In theory, 386 implementations could also have segmented pointers, but
such real implementations seem to be few and far between.

Dan
 
D

Dan Pop

In said:
Yes, I believe it is; thus the phrase "distinct type".

Consider a C implementation in which int and long have the same size,
signedness, and alignment requirements (such implementations are very
common).

Yes, but this is an artefact of the implementation, allowed but not
*required* by the standard. Another implementation is still free to have
int and long of different sizes. While the C++ wchar_t is still bound to
another, unspecified, integer type.
Agreed. As it is, though, "typedef" already creates an alias, and
changing that could break too much code; if we wanted to add this in a
future standard, we'd need a different keyword that creates a distinct
type.

That's what I said "would have been". As it is, the semantics of typedef
are cast in concrete, but this still doesn't prevent the addition of a
new keyword for creating "new" types, let's say "newtype", sharing the
syntax of typedef.
The problem is that it's non-portable code for which a compiler can't
easily generate a warning.

There are far too many instances of non-portable code for which a compiler
can't easily generate a warning.
If the code is unintentionally non-portable, I'd consider that an
immediate problem; I'd much rather fix it *before* I try to port the
code.

In the real world, the *only* portable code is code that has been ported.
The portability issues diagnosed by the compiler at porting time are, by
far, the most trivial ones.
I agree on the first point, but making size_t a distinct type could
make some existing correct (but non-portable) code illegal; I provided
an example above. I'm tempted to suggest that it's worth it.

I don't think that code relying on a property of size_t not guaranteed by
the standard qualifies as correct C code. It's either code that works by
accident or code written in FOO C (i.e. for a specific C implementation),
rather than Standard C.

Dan
 
C

Clive D. W. Feather

I came across this old article by Doug Gwyn:

Douglas A. Gwyn said:
Alexander said:
Funny. "The restriction that a byte is now exactly eight bits was a
conscious decision by the standard developers. It came about due to a
combination of factors, primarily the use of the type int8_t within
the networking functions and the alignment with the ISO/IEC 9899:1999
standard, where the intN_t types are now defined.
[...]

Yet another example (starting with the "feature test" macros
in 1988) where the POSIX people have not understood what was
suggested by the Standard C people. The networking functions
certainly should be using uint8_t since they explicitly deal
with octet streams, and POSIX can reasonably require support
for that typedef in the C implementation.

I am afraid that *you* don't understand what the issues were and why the
decision that was made was made.

I wasn't involved in much of the POSIX work, but my attention was drawn
to the networking functions at some point. I realized that insufficient
attention had been paid to the question of non-8-bit systems and
mentioned this on the appropriate list.

The problems occur when you try to relate C basic types to networking
concepts. For example, consider:

write (a_socket, buffer, 8)

on a system with 9-bit bytes but a networking infrastructure based
around octets. What should happen? One obvious answer is that the values
sent to the network are "buffer[0]&0x0FF", "buffer[1]&0x0FF", etc.
Another, slightly less obvious one, is that the 72 bits should be
reorganised as 9 octets to go on the wire.

Similarly, following a successful:

read (a_socket, buffer, 1)

what is the value of "buffer[0]&0x100"?

And then there's functions like htons and ntohs. What is the correct
definition of these functions on systems with 9-bit bytes? There were
also some other cases whose details I have forgotten.

Now *all these issues can be fixed* and I wrote definitions which I
believe fixed them, but the POSIX people decided that the complexity was
not something they wanted to deal with. And so they chose to solve it by
only allowing 8 bit bytes.
 
D

David Hopwood

Clive said:
I came across this old article by Doug Gwyn:

Douglas A. Gwyn said:
Alexander said:
Funny. "The restriction that a byte is now exactly eight bits was a
conscious decision by the standard developers. It came about due to
a combination of factors, primarily the use of the type int8_t
within the networking functions and the alignment with the ISO/IEC
9899:1999 standard, where the intN_t types are now defined.
[...]

Yet another example (starting with the "feature test" macros
in 1988) where the POSIX people have not understood what was
suggested by the Standard C people. The networking functions
certainly should be using uint8_t since they explicitly deal
with octet streams, and POSIX can reasonably require support
for that typedef in the C implementation.

Requiring support for that typedef is of course equivalent to only
allowing 8 bit bytes, so Gywn's argument here is not self-consistent.

Note that POSIX also requires SCHAR_MIN == -128. If it did not, then
there might only be 255 non-trap representations of char or int8_t,
which would also cause problems for file and network I/O.

http://www.opengroup.org/onlinepubs/009695399/basedefs/limits.h.html

David Hopwood <[email protected]>
 
D

David Hopwood

David said:
Requiring support for that typedef is of course equivalent to only
allowing 8 bit bytes, so Gywn's argument here is not self-consistent.

Sorry, I was mistaken. It is possible to support uint8_t even though
the size of a byte is larger.

David Hopwood <[email protected]>
 
K

Keith Thompson

David Hopwood said:
Sorry, I was mistaken. It is possible to support uint8_t even though
the size of a byte is larger.

It looks to me like you're right (in your last statement). If
CHAR_BIT > 8, then uint8_t has to have (at least) CHAR_BIT-8 padding
bits. ("At least" because uint8_t could be bigger than one byte.)
The "width" of an integer type excludes padding bits.
 
J

Jack Klein

It looks to me like you're right (in your last statement). If
CHAR_BIT > 8, then uint8_t has to have (at least) CHAR_BIT-8 padding
bits. ("At least" because uint8_t could be bigger than one byte.)
The "width" of an integer type excludes padding bits.

The C99 definition of the exact-width integer types does not allow for
this possibility in a (strictly?) conforming implementation.

========
7.18.1.1 Exact-width integer types

1 The typedef name intN_t designates a signed integer type with width
N, no padding bits, and a two’s complement representation. Thus,
int8_t denotes a signed integer type with a width of exactly 8 bits.

2 The typedef name uintN_t designates an unsigned integer type with
width N. Thus, uint24_t denotes an unsigned integer type with a width
of exactly 24 bits.

3 These types are optional. However, if an implementation provides
integer types with widths of 8, 16, 32, or 64 bits, it shall define
the corresponding typedef names.
========

I have built usable <stdint.h> headers for various C90'ish
implementations, in some cases subset versions because of no available
types larger than 32 bits.

On the CHAR_BIT 16 DSP I am working on mostly these days, there are no
int8_t or uint8_t types in the header I made, but there are
int_least8_t and int_fast8_t typedefs, all of which target
signed or unsigned int.

In reality, it is generally only communications interfaces where this
is really a problem, and even there it is not a problem in most cases
and not too much of one in others.

Typical communication peripherals (on- or off-chip) such as UART, i2C,
SPI, typically have 8-bit interfaces regardless of processor bus
width. As long as memory is not a problem, you ignore the upper bits
of a byte and the hardware device only sees the low eight anyway.

When you use interfaces with packed data (some CAN or USB or network
controllers), you need a routine at the driver level that
packs/unpacks the actual octets of the data between the application
and the hardware. I've done it, it's not hard, and it's transparent
to the application, as Clive said.

The vast majority of CHAR_BIT > 8 platforms in active development
today are DSPs. Most that do not offer octet addressing are either
16- or 32-bit, although I remember an old Motorola family that was
24-bit that is probably still around.

I don't know of a single hosted implementation for any DSP, and I
would be very surprised if anyone developing for them was dying for a
UNIX or Linux port, so it's pretty much of a moot point.
 
D

David Hopwood

Jack said:
The C99 definition of the exact-width integer types does not allow for
this possibility in a (strictly?) conforming implementation.

========
7.18.1.1 Exact-width integer types

1 The typedef name intN_t designates a signed integer type with width
N, no padding bits, and a two’s complement representation. Thus,
int8_t denotes a signed integer type with a width of exactly 8 bits.

2 The typedef name uintN_t designates an unsigned integer type with
width N. Thus, uint24_t denotes an unsigned integer type with a width
of exactly 24 bits.

3 These types are optional. However, if an implementation provides
integer types with widths of 8, 16, 32, or 64 bits, it shall define
the corresponding typedef names.

If I'm reading this correctly, it is possible to support uint8_t when
CHAR_BIT > 8, but not int8_t.

What was the motivation for requiring that signed integer types have
no padding bits, but not imposing the same requirement for unsigned
integer types?
The vast majority of CHAR_BIT > 8 platforms in active development
today are DSPs. Most that do not offer octet addressing are either
16- or 32-bit, although I remember an old Motorola family that was
24-bit that is probably still around.

I don't know of a single hosted implementation for any DSP, and I
would be very surprised if anyone developing for them was dying for a
UNIX or Linux port, so it's pretty much of a moot point.

I was once involved with a project that tried to port a TCP/IP stack
to a DSP with 32-bit words, though. No fun at all.

David Hopwood <[email protected]>
 
D

Douglas A. Gwyn

Clive said:
I came across this old article by Doug Gwyn:
Alexander said:
Funny. "The restriction that a byte is now exactly eight bits was a
conscious decision by the standard developers. It came about due to
a combination of factors, primarily the use of the type int8_t
within the networking functions and the alignment with the ISO/IEC
9899:1999 standard, where the intN_t types are now defined.
[...]
Yet another example (starting with the "feature test" macros
in 1988) where the POSIX people have not understood what was
suggested by the Standard C people. The networking functions
certainly should be using uint8_t since they explicitly deal
with octet streams, and POSIX can reasonably require support
for that typedef in the C implementation.
I am afraid that *you* don't understand what the issues were and why the
decision that was made was made. ...

Actually I understand those issues fine, and have been
involved in implementation of Internet protocols on
60-bit hosts, for example.

However, by dropping the majority of my posting of 24
Sep 2003, you have completely misrepresented what it
was discussing, which has nothing to do with issues
such as 9-bit bytes. Anyone who wants to see what I
was *actually* talking about needs to look at the
actual article, which you can find via Google groups
search using these keywords: Gwyn uint8_t freedom.
 
D

Douglas A. Gwyn

David said:
Requiring support for that typedef is of course equivalent to only
allowing 8 bit bytes, so Gywn's argument here is not self-consistent.

Unless you read my actual posting, you do not know
what I was arguing. Clive completely misrepresented it.
Note that POSIX also requires SCHAR_MIN == -128. If it did not, then
there might only be 255 non-trap representations of char or int8_t,
which would also cause problems for file and network I/O.

No, it wouldn't, because octets ought to be handled
via unsigned types.
 
D

Douglas A. Gwyn

David said:
What was the motivation for requiring that signed integer types have
no padding bits, but not imposing the same requirement for unsigned
integer types?

It's not necessary, due to corresponding signed and
unsigned types having to match up (requirement elsewhere).
 
B

Brian Inglis

If I'm reading this correctly, it is possible to support uint8_t when
CHAR_BIT > 8, but not int8_t.

The section heading "Exact-width integer types" has certain
implications, and it says both u/int8_t must have exactly 8 bits.

If CHAR_BIT > 8, the implementation can support u/int_least8_t, as JK
also stated, in the part you snipped.
What was the motivation for requiring that signed integer types have
no padding bits, but not imposing the same requirement for unsigned
integer types?

ICBW but are unsigned types allowed to have any padding bits?
I was once involved with a project that tried to port a TCP/IP stack
to a DSP with 32-bit words, though. No fun at all.

Hosted implementations are not likely, but that just makes support of
as much as useful of the standard library a QoI issue.
Porting network stacks is likely, and u/int_least8_t could help.
 
K

Keith Thompson

Douglas A. Gwyn said:
It's not necessary, due to corresponding signed and
unsigned types having to match up (requirement elsewhere).

I don't see an explicit requirement that signed and unsigned types
have to match up closely enough to preclude having an intN_t but not a
uintN_t for some value of N. For example, I can imagine something
like

INT_MIN == -2**23
INT_MAX == +2**23-1
UINT_MAX == +2**23-1

where signed int has no padding bits but unsigned int has one padding
bit. Such an implementation could have

typedef int int24_t;
typedef unsigned int uint23_t;

Does the standard preclude this?
 
D

Dan Pop

In said:
It looks to me like you're right (in your last statement). If
CHAR_BIT > 8, then uint8_t has to have (at least) CHAR_BIT-8 padding
bits. ("At least" because uint8_t could be bigger than one byte.)
The "width" of an integer type excludes padding bits.

Although the standard is less than perfectly clear, it seems that the
exact-width integer types cannot have padding bits. The requirement is
explicitly mentioned only in the paragraph dealing with signed types,
but I can see no reason why exact-width unsigned types should be any
different:

7.18.1.1 Exact-width integer types

1 The typedef name intN_t designates a signed integer type with
width N, no padding bits, and a two's complement
representation. Thus, int8_t denotes a signed integer type with
a width of exactly 8 bits.

2 The typedef name uintN_t designates an unsigned integer type
with width N. Thus, uint24_t denotes an unsigned integer type
with a width of exactly 24 bits.

Dan
 
W

Wojtek Lerch

Dan Pop said:
Although the standard is less than perfectly clear, it seems that the
exact-width integer types cannot have padding bits. The requirement is
explicitly mentioned only in the paragraph dealing with signed types,
but I can see no reason why exact-width unsigned types should be any
different:

Maybe they shouldn't, but they are. Since the requirement is only mentioned
in the paragraph dealing with signed types, it only applies to signed types.
This may be surprising and inconsistent, but it's not unclear.
 
D

Dan Pop

In said:
Maybe they shouldn't, but they are. Since the requirement is only mentioned
in the paragraph dealing with signed types, it only applies to signed types.
This may be surprising and inconsistent, but it's not unclear.

It's not so clear for those familiar with the history of the document.
As late as N869, the text in question was:

7.18.1.1 Exact-width integer types

[#1] The typedef name intN_t designates a signed integer |
type with width N. Thus, int8_t denotes a signed integer |
type with a width of exactly 8 bits.

[#2] The typedef name uintN_t designates an unsigned integer |
type with width N. Thus, uint24_t denotes an unsigned |
integer type with a width of exactly 24 bits. |

It is perfectly possible that the intent is to exclude padding bits from
all the exact width integer types. This is strongly suggested by the
name "exact-width". The addition made after N869 is already known to
be inconsistent, as it is missing from the following text:

3 These types are optional. However, if an implementation provides
integer types with widths of 8, 16, 32, or 64 bits, it shall
define the corresponding typedef names.

rendering 7.18.1.1p3 inconsistent with 7.18.1.1p1.

Dan
 

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

No members online now.

Forum statistics

Threads
473,781
Messages
2,569,615
Members
45,295
Latest member
EmilG1510

Latest Threads

Top