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

D

Douglas A. Gwyn

Thad said:
whether size_t is guaranteed to be big enough to hold a unique index to
every object at runtime.

The C standard specifies what size_t must be capable of.
An attempt by the program to use objects larger than that
would not be the implementation's fault.
 
D

Douglas A. Gwyn

pete said:
I've heard that there are some implementations which accept
void main(void){return;}
Does that code imply that there's any output?

Since that is not s.c. code by anybody's interpretation,
it is irrelevant to conformance issues. The C standard
has nothing to say about its behavior, since it doesn't
conform to the requirements on interfacing to the
(presumably hosted) implementation's invoking environment.
On such an implementation, would
int main(void){return 0;}
imply anything different ?

You'd have to ask the implementor (or consult the
implementation documentation).
 
P

pete

Keith said:
Hmm. The definition of the %p specifier says:

The argument shall be a pointer to void. The value of the pointer
is converted to a sequence of printing characters, in an
implementation-defined manner.

It doesn't explicitly use the word "behavior". I would guess that
doing something in an "implementation-defined manner" qualifies as
"implementation-defined behavior", but I'm not certain. I suppose the
key is whether this is "behavior" (defined as "external appearance or
action").

The word is "shall".

N869
4. Conformance
[#1] In this International Standard, ``shall'' is to be
interpreted as a requirement on an implementation or on a
program; conversely, ``shall not'' is to be interpreted as a
prohibition.
[#2] If a ``shall'' or ``shall not'' requirement that
appears outside of a constraint is violated, the behavior is
undefined. Undefined behavior is otherwise indicated in
this International Standard by the words ``undefined
behavior'' or by the omission of any explicit definition of
behavior. There is no difference in emphasis among these
three; they all describe ``behavior that is undefined''.
 
P

pete

Douglas said:
Since that is not s.c. code by anybody's interpretation,
it is irrelevant to conformance issues. The C standard
has nothing to say about its behavior, since it doesn't
conform to the requirements on interfacing to the
(presumably hosted) implementation's invoking environment.


You'd have to ask the implementor (or consult the
implementation documentation).

Then,
either the exit status isn't considered as
part of the program's output,
or
int main(void){return 0;}
isn't strictly conforming.

I think that the exit status isn't considered as
part of the program's output.
 
K

Kevin Easton

In comp.lang.c James Kuyper said:
LibraryUser said:
Kevin said:
... snip ...

This thread is probably as good as any to pose this question that
I came up with. I wonder if anyone can tell me what the output
of the following program is, when run on the C abstract machine?

#include <stdio.h>

unsigned char count_reps[sizeof(size_t *)] = { 0 };

This is already questionable, because it probably exceeds the
required minimum limits on data space specified by the standard.

On typical implementations, sizeof(size_t *) is about 4, sometimes 8.
The translation limit for the size of an object is 65535. Technically,
there's no guarantee that sizeof(size_t*) <= 65535, so count_reps could
in principle exceed that limit. However, I wouldn't characterize that as
"probable".

So, does this mean that the semantics of any program that defines a
variable of a type other than char, unsigned char or signed char is not
defined by the C abstract machine? (since sizeof(int) may be > 65535, so
even allocating a single int variable can't be guaranteed not to exceed
the minimum limit for the size of an object). If not then the program
can be trivially fixed by allocating count_reps as a size_t object and
then treating it as an array of unsigned chars.

- Kevin.
 
K

Kevin Easton

In comp.lang.c Thad Smith said:
Function r() will increment count_reps[0], then call itself. This will
continue until either count_reps[0] overflows, involving undefined
behavior, or the nesting of invocations of r() exceeds the capacity of
the machine, also invoking undefined behavior.

Not!

I missed that count_reps[] was an _unsigned_ char. Obviously that
eliminates the overflow possibility. Your real question is obviously
whether size_t is guaranteed to be big enough to hold a unique index to
every object at runtime. I think not.

No, not quite. The point is that, as far as I can see, the C abstract
machine is supposed to have defined behaviour for any recursion depth,
and it's also supposed to give every object with overlapping lifetimes a
unique address. The intent of the program was to demonstrate that these
are incompatible.

- Kevin.
 
R

Ross Ridge

Ross said:
When C programmers want to write portable programs, and they don't
always do, then in practice they want to write a sufficiently portable
program that will run under all the implementations they're willing
to spend the effort to get it running under. This means they want it
to be portable more than just one C implmentation, but seldom do they
want it to be portable to every conforming C implementation that exists,
or could possibly ever exist.

Douglas A. Gwyn said:
If so, then they aren't thinking very clearly. By
programming to the standard of strict conformance,
whenever feasible, then *ensure* that that much of
their code is portable to *any* conforming C
implementation with essentially no work. ...

It's seldom feasible to for a program to be strictly conforming, and
it's a lot work to ensure that program is. There's almost no room for
using floating point types because all the undefined and implementation
defined behaviours involved. Even with signed integer types you have
to be very careful.
... There are very few programmers wise enough to anticipate the
exact characteristics of every C implementation to which their code
might conceivably be ported;

There many more programmers that are able to anticipate accurately
where their code will be ported to than that have even a small clue of
everything involved in making their programs strictly conforming.
... history is full of examples of platform assumptions
that were never necessary but became embedded in code
and eventually caused major porting headaches.

Saying programmers shouldn't do dumb things like assuming 32-bit ints,
is far cry from saying programmers shouldn't assume two-complement
arithmetic or that "@" is a valid character. Not assuming 32-bit ints,
isn't too hard (but not that easy if you've never programmed for a
machine with different sized ints), and there are lots of enviroments
out there in use to that don't have 32-bit ints. For most projects it's
clearly a win to not to assume 32-bit ints. But there are only a few
conforming C implementations that don't use two-complement arithmetic
(only one that I know of), and not assuming two-complement arithmetic
isn't anywhere as easy as not assuming 32-bit ints.

Sure, when it's easy and simple to do, make your code more portable to
save yourself time and headaches later. And yes, there are lot of ways
you can do this, and yes, there are a lot people who make their code much
less portable than it easily could be. But don't waste your time trying
to make your programs strictly conforming, it's far easier and cheaper
to just fix the any portability problems as you actually encounter then
to try to achieve a such a costly gold plated level of portablilty.
And people programming to those coding standards have
created problems when their code is used in other
contexts.

It's not a problem for the people programming to those coding standards.

Ross Ridge
 
T

The Real OS/2 Guy

In comp.lang.c James Kuyper said:
LibraryUser said:
Kevin Easton wrote:

... snip ...

This thread is probably as good as any to pose this question that
I came up with. I wonder if anyone can tell me what the output
of the following program is, when run on the C abstract machine?

#include <stdio.h>

unsigned char count_reps[sizeof(size_t *)] = { 0 };

This is already questionable, because it probably exceeds the
required minimum limits on data space specified by the standard.

On typical implementations, sizeof(size_t *) is about 4, sometimes 8.
The translation limit for the size of an object is 65535. Technically,
there's no guarantee that sizeof(size_t*) <= 65535, so count_reps could
in principle exceed that limit. However, I wouldn't characterize that as
"probable".

So, does this mean that the semantics of any program that defines a
variable of a type other than char, unsigned char or signed char is not
defined by the C abstract machine? (since sizeof(int) may be > 65535, so
even allocating a single int variable can't be guaranteed not to exceed
the minimum limit for the size of an object). If not then the program
can be trivially fixed by allocating count_reps as a size_t object and
then treating it as an array of unsigned chars.

No, because an implementation can decide to use
- CHAR_MIN -32767
- CHAR_MAX 32767
- INT_MIN -32767
......

The only rule that helps is:

sizeof char <= sizeof int <= sizeof long <= sizeof long long (and the
same rule for unsigned)

Your implementation will tell you the real limits in limits.h - but
another implementation may have other limits. The guaratee is that no
limit can be lower than the ones defined in the standard - but one,
some or all can be bigger and differ from implementation to
implementation.

So if you need to write a strictly conforming application it is on you
to limit yourself on the limits the standard describes and not on the
ones your implementation defines, as your application may have wider -
but never smaller limits.
 
D

Douglas A. Gwyn

pete said:
Then,
either the exit status isn't considered as
part of the program's output,
or
int main(void){return 0;}
isn't strictly conforming.

That conclusion does not follow. You asked about
the difference between the behavior of a specific
incorrect program and a s.c. program. There is
no way to draw any conformance conclusion from
that; the C standard says nothing about the
behavior of the first program, which might or
might not behave identically to the second,
depending on details of a specific implementation.
 
K

Keith Thompson

pete said:
Keith said:
Hmm. The definition of the %p specifier says:

The argument shall be a pointer to void. The value of the pointer
is converted to a sequence of printing characters, in an
implementation-defined manner.

It doesn't explicitly use the word "behavior". I would guess that
doing something in an "implementation-defined manner" qualifies as
"implementation-defined behavior", but I'm not certain. I suppose the
key is whether this is "behavior" (defined as "external appearance or
action").

The word is "shall".

N869
4. Conformance
[#1] In this International Standard, ``shall'' is to be
interpreted as a requirement on an implementation or on a
program; conversely, ``shall not'' is to be interpreted as a
prohibition.
[#2] If a ``shall'' or ``shall not'' requirement that
appears outside of a constraint is violated, the behavior is
undefined. Undefined behavior is otherwise indicated in
this International Standard by the words ``undefined
behavior'' or by the omission of any explicit definition of
behavior. There is no difference in emphasis among these
three; they all describe ``behavior that is undefined''.

If you're referring to the "shall" in "The argument shall be a pointer
to void", that's not relevant. The program being discussed (which was
snipped a few replies ago) is:

/* 3 */
#include <stdio.h>
int main(void)
{
const char *s = "Hello, world\n";
printf("The string literal is stored at address %p\n", (void*)s);
return 0;
}

The argument already is a pointer to void.

The implementation-defined behavior I was referring to was the manner
in which the pointer is converted to a sequence of characters by
printf's "%p" conversion.

(I suspect the implementation's choice for the address of the string
literal invokes some kind of unspecified behavior that also prevents
the program from being s.c., but I'm too lazy to track down the
citations right now.)
 
B

Brian Inglis

Stewart Brodie wrote:
...
Whether you agree or not, I think that the reasoning goes like this: If the
return value from main is considered "output", and since the size of int is
implementation-defined[*], the program cannot be strictly conforming as this
would constitute output which depends on implementation-defined behaviour.

That's not quite it. Whether or not the output is "dependent upon"
implementation-defined behavior should, I think, be evaluated with
respect to the values in the output, not their representation.

The tricky question is the fact that a return value from main() is
equivalent to passing that value to exit(). The value passed to exit()
is NOT said to be passed back to the calling environment (though that is
indeed what happens in many Unix and Unix-like operating systems).
Instead, a return value of 0 or EXIT_SUCCESS from main() causes "an
implementation-defined form of the status _successful termination_" to
be returned by the program itself. A return value of EXIT_FAILURE from
main() causes "an implementation-defined form of the status
_unsuccessful termination_" to be returned by the program itself. All
other cases result in an implementation-defined status being returned by
the program. In a Unix-like environment, the exit status is an integer
value; but that's not required by the C standard. A conforming
implementation of C could turn on a green light for "_successful
termination_" and a red one for "_unsuccessful termination_", and a
yellow one for all other values.

The fact the form of the exit status is implementation-defined is the
core of the issue. It's debatable whether the exit status counts as
output - I believe that it should. I gather from Doug's arguments that
it's the value which is passed to exit() which he considers the true
output; that the exit status is outside the scope of the standard. I
would agree that this is a reasonable approach for defining conformance;
I just don't see anything in the definition of "strictly conforming"
that actuall says that. The value passed to exit() never makes it out of
the program; it can't count as output. The exit status that it causes to
be returned by the program does make it out, so it should count as
output.

However, I'm not pushing this issue as strongly as the corresponding
issue the standard I/O functions. That's because I'm not entirely clear
on one issue: does behavior which produces as output an
implementation-defined translation of the exit status count as output
dependent on implementation-defined behavior? The behavior (at least for
the values 0, EXIT_SUCCESS, and EXIT_FAILURE) could arguably be
described as standard-defined; it's the status, not the behavior, which
is implementation-defined. I'm not sure this is a valid argument, but my
uncertainty about the matter makes me unwilling to push it. If it is not
a valid argument, then the set of strictly conforming programs would
consist only of programs that never exit.


Thanks. Take care, Brian Inglis Calgary, Alberta, Canada
 
B

Brian Inglis

Stewart Brodie wrote:
...
Whether you agree or not, I think that the reasoning goes like this: If the
return value from main is considered "output", and since the size of int is
implementation-defined[*], the program cannot be strictly conforming as this
would constitute output which depends on implementation-defined behaviour.

That's not quite it. Whether or not the output is "dependent upon"
implementation-defined behavior should, I think, be evaluated with
respect to the values in the output, not their representation.

The tricky question is the fact that a return value from main() is
equivalent to passing that value to exit(). The value passed to exit()
is NOT said to be passed back to the calling environment (though that is
indeed what happens in many Unix and Unix-like operating systems).
Instead, a return value of 0 or EXIT_SUCCESS from main() causes "an
implementation-defined form of the status _successful termination_" to
be returned by the program itself. A return value of EXIT_FAILURE from
main() causes "an implementation-defined form of the status
_unsuccessful termination_" to be returned by the program itself. All
other cases result in an implementation-defined status being returned by
the program. In a Unix-like environment, the exit status is an integer
value; but that's not required by the C standard. A conforming
implementation of C could turn on a green light for "_successful
termination_" and a red one for "_unsuccessful termination_", and a
yellow one for all other values.

In a Unix environment, the OS status returned by the program is
normally the low eight bits of the C status passed to exit(), so
all integer multiples of 256 appear as /successful termination/,
and all other values appear as /unsuccessful termination/.

On IBM MF OSes, OS status must be a multiple of 4 between 0 and
16 IIRC, so EXIT_SUCCESS could be 0, EXIT_FAILURE could be 16,
and any mapping of other values is allowable.

On VMS IIRC, any odd OS status is /successful termination/, any
even OS status is /unsuccessful termination/, and generates an
error message depending on the higher bits of the status.
The fact the form of the exit status is implementation-defined is the
core of the issue. It's debatable whether the exit status counts as
output - I believe that it should. I gather from Doug's arguments that
it's the value which is passed to exit() which he considers the true
output; that the exit status is outside the scope of the standard. I
would agree that this is a reasonable approach for defining conformance;
I just don't see anything in the definition of "strictly conforming"
that actuall says that. The value passed to exit() never makes it out of
the program; it can't count as output. The exit status that it causes to
be returned by the program does make it out, so it should count as
output.

However, I'm not pushing this issue as strongly as the corresponding
issue the standard I/O functions. That's because I'm not entirely clear
on one issue: does behavior which produces as output an
implementation-defined translation of the exit status count as output
dependent on implementation-defined behavior? The behavior (at least for
the values 0, EXIT_SUCCESS, and EXIT_FAILURE) could arguably be
described as standard-defined; it's the status, not the behavior, which
is implementation-defined. I'm not sure this is a valid argument, but my
uncertainty about the matter makes me unwilling to push it. If it is not
a valid argument, then the set of strictly conforming programs would
consist only of programs that never exit.

I'd say based on the standard (illustrated by the examples given
above), using only the standard defined values can be considered
strictly confirming, and I'd question whether a program using any
other value could even be considered conforming, as the result
could be the opposite of that intended, depending on the
platform.

Thanks. Take care, Brian Inglis Calgary, Alberta, Canada
 
D

Douglas A. Gwyn

Brian said:
I'd say based on the standard (illustrated by the examples given
above), using only the standard defined values can be considered
strictly confirming,

Insofar as compliance criteria go that is certainly the intent
(assuming that no other "output" depends on implementation
parameters).
and I'd question whether a program using any
other value could even be considered conforming, as the result
could be the opposite of that intended, depending on the
platform.

A "conforming" program merely has to work on one implementation;
it might not even be successfully compiled on another, much less
have the same behavior. That's why this particular category has
no relevance for standards compliance.
 
K

Kevin Easton

In comp.lang.c Ross Ridge said:
Saying programmers shouldn't do dumb things like assuming 32-bit ints,
is far cry from saying programmers shouldn't assume two-complement
arithmetic or that "@" is a valid character. Not assuming 32-bit ints,
isn't too hard (but not that easy if you've never programmed for a
machine with different sized ints), and there are lots of enviroments
out there in use to that don't have 32-bit ints. For most projects it's
clearly a win to not to assume 32-bit ints. But there are only a few
conforming C implementations that don't use two-complement arithmetic
(only one that I know of), and not assuming two-complement arithmetic
isn't anywhere as easy as not assuming 32-bit ints.

Huh? Is it *really* that hard to avoid direct access to the underlying
representation of an integer object? Well, OK, programs that directly
send integers across a network do this, but you'll already have some
modules in the program that are platform-specific in order to access the
network anyway, so you might as well put the twos-complement assumption
in those.

I feel a far more common example is assuming decent quality
floating-point.

- Kevin.
 
K

Keith Thompson

Brian Inglis said:
On IBM MF OSes, OS status must be a multiple of 4 between 0 and
16 IIRC, so EXIT_SUCCESS could be 0, EXIT_FAILURE could be 16,
and any mapping of other values is allowable.

MF is short for mainframe, right? (I'm afraid that wasn't the first
expansion I thought of.)
On VMS IIRC, any odd OS status is /successful termination/, any
even OS status is /unsuccessful termination/, and generates an
error message depending on the higher bits of the status.

As a special case, VMS C implementations map exit(0) to an odd
(successful) OS status. They don't do any special-case mapping for
exit(1). I've seem a lot of VMS C programs (programs written
specifically on and for VMS) that incorrectly use exit(1) to indicate
an error.

Old habits die hard. We've had the EXIT_SUCCESS and EXIT_FAILURE
macros since C89, but too many programmers still use 0 and 1.
 
R

Ross Ridge

Ross Ridge said:
But there are only a few conforming C implementations that don't use
two-complement arithmetic (only one that I know of), and not assuming
two-complement arithmetic isn't anywhere as easy as not assuming 32-bit
ints.

Kevin Easton said:
Huh? Is it *really* that hard to avoid direct access to the underlying
representation of an integer object?

That's not the only way to make assumptions about twos-complement
arithmetic. A trivial example is having "int a = -32768;" in your code
is going to make your program not strictly conforming.

Ross Ridge
 
K

Kevin Easton

In comp.lang.c Ross Ridge said:
That's not the only way to make assumptions about twos-complement
arithmetic. A trivial example is having "int a = -32768;" in your code
is going to make your program not strictly conforming.

That's the same mistake as putting

int a = -32769;

in your code - it's just the assumption about int having a range larger
it's guaranteed to.

- Kevin.
 
D

Douglas A. Gwyn

Ross said:
That's not the only way to make assumptions about twos-complement
arithmetic. A trivial example is having "int a = -32768;" in your code
is going to make your program not strictly conforming.

Gee, there must be an awful lot of code containing that.
 
P

Paul Eggert

Saying programmers shouldn't do dumb things like assuming 32-bit ints,

It's not at all dumb to assume that int is at least 32 bits wide.
POSIX 1003.1-2001 (another ISO standard) requires it, as do the GNU
coding standards. Lots of portable C code safely assumes it.

No doubt there are still some C platforms with 16-bit int, or other
widths less than 32 bits, but such platforms are not of much interest
these days to authors of a wide class of portable software.
 
D

Douglas A. Gwyn

Paul said:
> It's not at all dumb to assume that int is at least 32 bits wide.
> POSIX 1003.1-2001 (another ISO standard) requires it, as do the GNU
> coding standards. Lots of portable C code safely assumes it.
> No doubt there are still some C platforms with 16-bit int, or other
> widths less than 32 bits, but such platforms are not of much interest
> these days to authors of a wide class of portable software.

In other words to VAX^H^H^HGNU programmers.
Many embedded systems have C implementations with 16-bit int.
Porting code that assumes int has more bits than the standard
guarantees is a pain, and unnecessarily so. If you want to
use a standard C type with width at least 32 bits, use long.
 

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,146
Messages
2,570,832
Members
47,375
Latest member
FelishaCma

Latest Threads

Top