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

L

LibraryUser

Keith said:
.... snip ...

Consider three sample programs:

/* 1 */
int main(void)
{
return 0;
}

/* 2 */
#include <stdio.h>
int main(void)
{
printf("Hello, world\n");
return 0;
}

/* 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;
}

We can all agree, I think, that the first program is strictly
conforming and the third is not, since it produces output
dependent on unspecified and/or implementation-defined behavior.

I would not necessarily agree. I believe the third should also
be considered strictly conforming. It is simply spitting back
something about its own environment. At the same time, the fact
that the representation from %p is implementation defined is a
point in favor of your attitude. But the fact that that
"address" can differ is not germane.
 
K

Kevin Easton

In comp.lang.c Douglas A. Gwyn said:
There are actually people who have argued to the effect that
not even this program can be s.c. Of course they're wrong,
but they apparently have gained adherents.

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 };

void r(void)
{
size_t i = 0;

do {
count_reps++;
} while ((count_reps == 0) && (++i < sizeof count_reps));

if (i < sizeof count_reps)
r();
else
puts("More size_t objects with overlapping lifetimes "
"exist than there are possible unique size_t * values - "
"this means there are distinct objects with overlapping "
"lifetimes that do not have distinct addresses!");
}

int main()
{
r();

return 0;
}

- Kevin.
 
L

LibraryUser

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.
 
K

Kevin Easton

In comp.lang.c LibraryUser said:
I would not necessarily agree. I believe the third should also
be considered strictly conforming. It is simply spitting back
something about its own environment. At the same time, the fact
that the representation from %p is implementation defined is a
point in favor of your attitude. But the fact that that
"address" can differ is not germane.

I believe the intention behind SC was that a program that, for example,
prints the value of INT_MAX from limits.h is not SC.

- Kevin.
 
J

James Kuyper

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".
 
T

Thad Smith

Kevin said:
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 };

void r(void)
{
size_t i = 0;

do {
count_reps++;
} while ((count_reps == 0) && (++i < sizeof count_reps));

if (i < sizeof count_reps)
r();
else
puts("More size_t objects with overlapping lifetimes "
"exist than there are possible unique size_t * values - "
"this means there are distinct objects with overlapping "
"lifetimes that do not have distinct addresses!");
}

int main()
{
r();

return 0;
}


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.

The output of the program is undefined by the C Standard.

Thad
 
D

Dan Pop

In said:
There are actually people who have argued to the effect that
not even this program can be s.c. Of course they're wrong,
but they apparently have gained adherents.

It's a matter of whether the program's exit status is considered as
part of the program's output. If it is, this program is not strictly
conforming.

Dan
 
T

Thad Smith

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.

Thad
 
T

Thad Smith

P.J. Plauger said:
But it doesn't take too much imagination to arrive at that conclusion,
does it? (IIRC, it's also widespread knowledge.)

The Standard requirement for an implementation-defined aspect is that
the implementor is required to _document_ this choice as part of the
implementation. Without that designation in the Standard, doesn't the
feature officially invoke unspecified behavior?

On a practical level, I expect reasonable implementations to document
the size of int, but I don't think it can be considered officially
implementation-defined with specific standard requirement.

Thad
 
C

Christopher Benson-Manica

It's a matter of whether the program's exit status is considered as
part of the program's output. If it is, this program is not strictly
conforming.

Can you elaborate on what you mean here? I'm sure I'm not following...
 
S

Stewart Brodie

Christopher Benson-Manica said:
Can you elaborate on what you mean here? I'm sure I'm not following...

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.


[*] If this is the case, I really do think that it should be stated
explicitly in 6.2.5p5, unless it is specified elsewhere and I've not
noticed.
 
J

James Kuyper

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.
 
K

Keith Thompson

LibraryUser said:
Keith Thompson wrote: [...]
/* 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;
}

We can all agree, I think, that the first program is strictly
conforming and the third is not, since it produces output
dependent on unspecified and/or implementation-defined behavior.

I would not necessarily agree. I believe the third should also
be considered strictly conforming. It is simply spitting back
something about its own environment. At the same time, the fact
that the representation from %p is implementation defined is a
point in favor of your attitude. But the fact that that
"address" can differ is not germane.

The third program deliberately produces output that depends on
implementation-defined behavior. If you want it to qualify as
strictly conforming, then you're advocating radically changing the
meaning of the term, not just fixing the definition.

I wouldn't mind another conformance class, somewhere between
"conforming" and "strictly conforming", that would cover this program,
but I think that "strictly conforming" is (at least potentially) a
useful enough concept that we don't want to weaken it too much.
 
P

pete

James said:
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.

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

On such an implementation, would
int main(void){return 0;}
imply anything different ?
 
P

pete

Thad said:
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.

I think that an implementation is allowed to support
muliple objects of SIZE_MAX size, in which case you might
have more byte size objects than you could count with size_t.
 
P

pete

Stewart said:
To clarify,
the standard does not explicitly say that the size of an int is
implementation-defined.

How explicit does it have to be ?

N869
6.5.3.4 The sizeof operator
[#4] The value of the result is implementation-defined
 
L

lawrence.jones

In comp.std.c Keith Thompson said:
The third program deliberately produces output that depends on
implementation-defined behavior.

Exactly what implementation-defined *behavior* does it depend on?

-Larry Jones

Even if lives DID hang in the balance, it would depend on whose they were.
-- Calvin
 
K

Keith Thompson

Exactly what implementation-defined *behavior* does it depend on?

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").
 
D

Douglas A. Gwyn

Stewart said:
No it is not, unless you are trying to write code that is strictly
conforming - that is why a great many (most?) real-world programmers ignore
the conformance criteria completely instead of trying to remain within its
bounds as far as possible.

No, it's sloppy because it impedes portability (the code would
fail when ported to the embedded processor I most work with
these days), and it is *unnecessary* to make the code depend
on that particular characteristic of the implementation.
There are simple, convenient, and well-known methods for
avoiding such nonportability.

Blaming "strict conformance" for this nonportable practice
is absurd.
Realistically, our portability goals can only include portability to targets
which have an ILP32 model.

That's pretty sad. All the world's still a VAX?
The C presentation of the host ABI is defined in terms of basic C types like
'int'. Ideally, you would use typedefs like intN_t, but you can't rely on
that as those types are optional. So you end up with non-guaranteed sizes
like int_leastN_t - which, by definition, may not be N-bits and thus not
match your host ABI. The solution is probably to redefine the ABI
definition is terms of intN_t and require support for those optional types
from implementations wishing to interoperate with that ABI.

If you need exact-width types for genuine functional reasons,
then by all means use the intN_t typedefs. If they don't
exist on some platform to which your code is ported you will
at least get a compiler warning, which is better than silent
acceptance of code when its assumptions are not being met.
To clarify, the standard does not explicitly say that the size of an int is
implementation-defined.

It wouldn't matter if it were, if what you are after is some
guarantee of a minimum width and not the storage size. The
key is to make the code work correctly with whatever the
(conforming) implementation happens to provide.
 

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,147
Messages
2,570,833
Members
47,380
Latest member
AlinaBlevi

Latest Threads

Top