A similar lint question (and questions about casts)
Daryl Clevenger
dlc at dlc.fac.cs.cmu.edu
Thu Dec 1 23:05:16 AEST 1988
I am by no means a novice C user and have the pleasure of knowing some
very competent programs here at work. Much of my knowledge of what is
portable and correct C came from this news group and its existence I
consider invaluable. There are still a few areas where I need clarification.
I will try to phrase the questions as clearly as possible, but I do not
think of them as naive. I am trying to consider them from "basic principles",
if such a notion can be applied to C, similar to examining something in
physics from "basic" or "axiomatic" natural laws.
First, a lint question along the lines of the current discussion.
Whenever I write code from scratch, I use lint before I ever
try to compile it and fix as many of the warnings/errors as I
can (modulo those things that lint will not shut up about). As a
result, I give practically every paranoia flag to lint as possible,
namely '-hca' (no -p, it seems to cause more problems than it
is worth and I really don't care about GCOS implementations :-)).
The question arises concerning casts and really leads to some generic
questions about them. Let's say you have the following code fragment
(assume all the correct include files and correct assignments/code in
the ellipses):
/* Exhibit 1 */
{
.
.
.
struct sockaddr_in saddr;
int s, len;
.
.
.
len = sizeof(struct sockaddr_in);
if (bind(s, (struct sockaddr *)&saddr, len) < 0) {
perror("bind");
exit(1);
}
.
.
.
}
Every time I lint this, I get "warning: illegal structure pointer combination"
for the bind() call. Here is the lint prototype from llib-lc:
int bind(s, n, l) struct sockaddr *n; { return 0; }
The questions are:
1) Given 2 structure pointers, is it portable to cast from one to
the other? I remember a discussion a while back and I believe
that the consensus was that "struct <name> *" pointers must
"smell the same" or problems may result. My hunch guess is
that technically structure pointer casts are not *DEFINED* to be
always portable, but in practice, they are (see further
discussion below).
2) In general, if a pointer cast meets the following constraints
from K&R 1st Ed., page 210, sec 14.4 (We love quoting this don't
we :-))
"A pointer to one type may be converted to a pointer to
another type. The resulting pointer may cause addressing
exceptions upon use if the subject pointer does not refer
to an object suitably aligned in storage. It is guaranteed
that a pointer to an object of a given size may be converted
to a pointer to an object of a smaller size and back again
without change."
(Note: from the above quoted paragraph, I assume that "smaller size"
really means "less than or equal size." If this is not
the case then replace the following occurences of "<="
with "<" and accordingly warp you brain to follow the
questions.)
From this I conclude:
For non-{floating,pointer} scalar types, if we have type1 *t1,
type2 *t2, type1 *tmp and
sizeof(type2) <= sizeof(type1), then the following is guaranteed
/* Exhibit 2 */
tmp = t1; /* tmp <==> t1 */
t2 = (type2 *)t1; /* No usage to cause addressing exceptions;
assignment only for effect */
t1 = (type1 *)t2;
if (tmp == t1) /* This is guaranteed from last statement
printf("True\n"); in quoted passage. */
This is the easy case, since a partial ordering is defined on
the set of scalar types such that, (let s <- sizeof operator)
/* s(void) <= */ s(char) <= s(short) <= s(int) <= s(long)
(NB: I did not include floating point types because I feel that
there is no guarantee what the sizeof(float) is, except
/* s(void) <= */ s(char) <= s(float). Pointer types
are not included since the rules governing them can be
easily deduced once this mess is made clear :-))
The problem I see (and my own answer to (1), but I would like
confirmation/denial) is with aggregate types (using structures
as an instance).
Given struct type1 *t1, struct type2 *t2 and struct type1 *tmp,
if (sizeof(struct type2) <= sizeof(struct type1) then
the code fragment under /* Exhibit 2 */ still is
guaranteed to work;
otherwise,
all bets are off.
(I am assuming the same is true for other aggregate types.
Lint still "fails" to get this right if it is indeed true.
When types differ, lint complains about all struct pointer
casts, regardless of the sizes of the structure objects.)
In none of the above discussion am I considering the act of *derefencing*
any of these pointers; that is an entirely different question whose
answer I understand :-). I am only talking about explicit coercions
between pointer types. My gut feeling is that struct pointer casts must
obey the above object size rules to portably work, but in practice all
struct pointers "smell the same."
3) Given pointer, p, obtained via legal coercion, is it safe, i.e.
no exception will be raised, to deference p if p is suitably aligned?
The results of derefencing such a pointer, p, may not be meaningful
in any portable way, depending on the type of (*p) and the type from
which p was obtained. Namely, does there exist 2 data types, T1
and T2, such that
T1 *s, T2 *p;
.
.
.
p = (T2 *)s; /* Assume s is properly initialized and the result
of the cast is a legal in all aspects *pointer*
of type T2. */
derefence(p), where derefence is a proper operator for type
T2 *, will *ALWAYS* have both a consistent and meaningful
result i.e. is 100% portable.
I realize that (3) is a rather nebulous question, but I am interested in
"correct" answers i.e. answers as defined in K&R or the ANSI Draft Standard
with "answers according to reality" as parenthetical answers.
4) Is my interpretation of the quoted paragraph from K&R accurate,
or am I totally wacked in the head and need to seek professional
help.
This is all of the questions, and the rest can be ignored and is merely
comments about this group. Pardon the run on sentences.
The first few years, all I knew about C was what was in K&R 1st Ed. with
contributions from my boss. The only environment I used was a 4.1BSD
VAX. Then I began reading this newsgroup, and I was enlightened :-).
The many contributions from Doug Gwyn, Karl Heur, Henry Spencer and
Chris Torek (as well as others that are only omitted because I can
not recall their names) have been invaluable to me. Many of the "dark
corners" of C were lit for me and I realized that I never *really*
understood the concept of portable C code until I read this group.
My appreciation of the time contributed by the "regulars" in this group
to edify the world can not be overstated.
Finally, while composing this article, I realized that I should have
saved and should save all the answers, especially from the above four
individuals, and use them to "educate" all the people I encounter that
write stupid/bad C code. In fact, maybe I should go on an apostolic
crusade of the world for the benefit of spreading the truth about
correct and portable C code :-). Well, maybe not until I am caught
up at work.
--------
Daryl Clevenger
Carnegie-Mellon CS/RI Facilities Staff
ARPA: dlc at cs.cmu.edu
--
More information about the Comp.lang.c
mailing list