is const necessary in eg int compar(const void *, const void *)

N

Nick Keighley

The conversion is implementation defined, and one of the possible
behaviors is undefined behavior.

I didn't think that was so. I thought Implementation Defined
actually had to be defined by the implementation. And that the
standard usually (nearly always?) gave a narrow range of
possibilities.

<find standard>

Para 3.3.4 "Cast Operators"
[...]
"A pointer may be converted to an integral type. The size of
integer required and the result is implementaion-defined. If
the space provided is not long enough, the behaviour is
undefined"

Seems to be having it both ways...

The Rationale has this to say:

"Nothing portable can be said about casting integers to pointers,
or vice versa, since the two are now incommensurate."

which sounds very like UB to me...
 
J

jameskuyper

Nick said:
I didn't think that was so. I thought Implementation Defined
actually had to be defined by the implementation. And that the
standard usually (nearly always?) gave a narrow range of
possibilities.

It does, sort of:

6.3.2.3p5: "An integer may be converted to any pointer type. Except as
previously specified, the result is implementation-defined, might not
be correctly aligned, might not point to an entity of the referenced
type, and might be a trap representation."

For void*, the possibility of incorrect alignment is not a problem.
Its not even possible for the resulting pointer to point to an entity
of the referenced type, so that's not an issue, either. The problem is
the last item on the list of possibilities. There's no problem if the
implementation-defined behavior does not include trap representations,
but portable code cannot make any such assumption. The behavior is
undefined if you store the result of such a conversion in an object.

It is an oddity of the standard that the possible result of a
conversion, which is a value, is referred to as a "representation",
something that can only be stored in an object. All the nasty things
that the standard says explicitly about trap representations apply
only if you try to store them in an object using an lvalue of pointer
type, or if you use an object that has had such had such a
representation created in it by other means. This would seem to imply
that it should be perfectly safe to make use of a trap representation
returned by a conversion, so long as you don't use it any way that
would otherwise result in it being stored in an object. However, I
strongly suspect that you can produce an argument that it is
implicitly undefined behavior to do anything with such a trap
representation, due to lack of an explicit definition of the behavior.
 
T

Tim Rentsch

Richard Heathfield said:
Nate Eldredge said:



I'm of the opinion that it's always possible, and usually preferable, to
avoid casting.

Casting is necessary for converting pointers to function. OTOH,
converting pointers to functions hardly ever arises.

Converting between arithmetic types is always possible without
casting, by simple assignment.

Conversion between integer types and pointer types requires
casting, except that a pointer may be assigned to a _Bool.

Any object/incomplete pointer type may be converted to any other
object/incomplete pointer type without casting. Exercise for
the reader: how can a (const int *) be converted to an (int *)
without casting?

IMO casting is preferable when avoiding it would introduce one or
more temporary variables solely to avoid having to cast,
especially if used only once.
 
T

Tim Rentsch

jameskuyper said:
It does, sort of:

6.3.2.3p5: "An integer may be converted to any pointer type. Except as
previously specified, the result is implementation-defined, might not
be correctly aligned, might not point to an entity of the referenced
type, and might be a trap representation."

For void*, the possibility of incorrect alignment is not a problem.
Its not even possible for the resulting pointer to point to an entity
of the referenced type, so that's not an issue, either. The problem is
the last item on the list of possibilities. There's no problem if the
implementation-defined behavior does not include trap representations,
but portable code cannot make any such assumption. The behavior is
undefined if you store the result of such a conversion in an object.

It is an oddity of the standard that the possible result of a
conversion, which is a value, is referred to as a "representation",
something that can only be stored in an object. All the nasty things
that the standard says explicitly about trap representations apply
only if you try to store them in an object using an lvalue of pointer
type, or if you use an object that has had such had such a
representation created in it by other means. This would seem to imply
that it should be perfectly safe to make use of a trap representation
returned by a conversion, so long as you don't use it any way that
would otherwise result in it being stored in an object. However, I
strongly suspect that you can produce an argument that it is
implicitly undefined behavior to do anything with such a trap
representation, due to lack of an explicit definition of the behavior.

Yes; it's a simple argument.

Any attempt to use the value of a trap representation (using the
type where it's a trap representation) certainly should be
undefined behavior, since trap representations don't represent a
value of that type. The Standard is quite clear about this:
the absence of definition means undefined behavior just the same
as if it were labelled undefined behavior.

Arguably a trap representation evaluated as a (void) expression
doesn't have its value used, so this might be okay. But anywhere
else a trap representation occurs in a value context, including
further conversion, needs to use the value and hence is undefined
behavior.
 
C

CBFalconer

Tim said:
.... snip ...

Yes; it's a simple argument.

The point is that 'implementation defined' is not universal. One
implementation may define it one way, another in some other
manner. So you can't count on code that invokes that.

Thus using it is not portable C. And portable C, as defined by the
C standard, is the subject of discussion on c.l.c. If you go to a
newsgroup that deals with _your_ system, it will be topical.
 
T

Tim Rentsch

Eric Sosman said:
Richard said:
(e-mail address removed)0m said:


Yes.

Well, either "yes" or "no" depending on what "necessary" means.

Yes, it is "necessary," because qsort() and bsearch() specify
`const' as part of the signature of the comparison function they
call. If you try to pass a pointer to a function with a different
signature, the compiler will complain and may reject your code. So
`const' is "necessary" if you want to be sure the code compiles.
[...]

I'm sure a lot of readers know this but for those that don't...

In C the correct term is "type" and not "signature". The term
"signature" comes from the programming language Russell, where
it's important to use a word different from "type" because "type"
means something different in Russell (related to the common
meaning of type, but still significantly different).

It's common in programming language circles to use the word
"signature" rather than "type" (especially when talking about
functions, for some reason), even when "type" is adequate and also
more accurate in the sense that it reflects the terminology used
by the programming language under discussion. I don't know why
people do this; my guess is because it sounds like it's more
precise or perhaps more grounded in some formal theoretical
framework. Neither of those is true, by the way.

There is an important sense in which "signature" is inappropriate
for C, in that functions in Russell (with signatures) are more
powerful than functions in C (with types). In particular, a
function in Russell admits parameters that are abstract data types
(what Russell calls a "type"); thus a function in Russell is like
a function template in C++, except that unlike templates in C++ a
function in Russell doesn't need to be instantiated to be used
with different type parameters -- in Russell abstract data types
are values and can be used just like ordinary values (such as int)
can. C doesn't admit abstract data types as values or parameters.

So if you're talking to someone about C and the term "signature"
comes up, it might be a good idea to ask "Do you mean type, or
are you talking about a different language?"

None of the above is intended as any kind of comment on Mr. Sosman,
who in my experience is both a gracious guy and a generally smart
fellow.
 
T

Tim Rentsch

CBFalconer said:
The point is that 'implementation defined' is not universal. One
implementation may define it one way, another in some other
manner. So you can't count on code that invokes that.

Thus using it is not portable C. And portable C, as defined by the
C standard, is the subject of discussion on c.l.c. If you go to a
newsgroup that deals with _your_ system, it will be topical.

If you read my posting (repeated below) again, I think you'll see
that it's a response to a sub-comment made by James Kuyper about
trap representations, and doesn't have anything to do with
implementation-defined behavior. I left in the earlier material,
about implementation-defined behavior, to help provide context;
I'm sorry if it being left in confused you.


ORIGINAL POSTING:

jameskuyper said:
It does, sort of:

6.3.2.3p5: "An integer may be converted to any pointer type. Except as
previously specified, the result is implementation-defined, might not
be correctly aligned, might not point to an entity of the referenced
type, and might be a trap representation."

For void*, the possibility of incorrect alignment is not a problem.
Its not even possible for the resulting pointer to point to an entity
of the referenced type, so that's not an issue, either. The problem is
the last item on the list of possibilities. There's no problem if the
implementation-defined behavior does not include trap representations,
but portable code cannot make any such assumption. The behavior is
undefined if you store the result of such a conversion in an object.

It is an oddity of the standard that the possible result of a
conversion, which is a value, is referred to as a "representation",
something that can only be stored in an object. All the nasty things
that the standard says explicitly about trap representations apply
only if you try to store them in an object using an lvalue of pointer
type, or if you use an object that has had such had such a
representation created in it by other means. This would seem to imply
that it should be perfectly safe to make use of a trap representation
returned by a conversion, so long as you don't use it any way that
would otherwise result in it being stored in an object. However, I
strongly suspect that you can produce an argument that it is
implicitly undefined behavior to do anything with such a trap
representation, due to lack of an explicit definition of the behavior.

Yes; it's a simple argument.

Any attempt to use the value of a trap representation (using the
type where it's a trap representation) certainly should be
undefined behavior, since trap representations don't represent a
value of that type. The Standard is quite clear about this:
the absence of definition means undefined behavior just the same
as if it were labelled undefined behavior.

Arguably a trap representation evaluated as a (void) expression
doesn't have its value used, so this might be okay. But anywhere
else a trap representation occurs in a value context, including
further conversion, needs to use the value and hence is undefined
behavior.
 

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

Similar Threads


Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top