A similar lint question (and questions about casts) 
Author Message
 A similar lint question (and questions about casts)

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


--



Wed, 19 May 1993 13:05:00 GMT  
 A similar lint question (and questions about casts)

Quote:

>        struct sockaddr_in saddr;
>        if (bind(s, (struct sockaddr *)&saddr, len) < 0) {
>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; }
>    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.

The conversion is always permitted, but use of the resulting pointer
is valid only if the original pointer was to an object suitably aligned.
Structure pointers all must have the same representation (this can be
deduced from other requirements), but not all structure types need be
aligned the same.  Thus "lint" is right to warn about this.  The ideal
solution here is to use a union of the two structure types, and pass
the address of the appropriate union member to the function.

Quote:
>    2) ...

It is the object alignments that matter, not their sizes (K&R 1st Ed.
was not sufficiently precise).  In general you cannot deduce alignment
from size or vice versa.

Quote:
>    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?

No, for example the size of the supposed object pointed to after the
conversion may exceed the size of the storage actually allocated.

There are portable uses of pointer conversion.  The general rule of
thumb is, if a pointer to a type really does point to an object of
that type, it may safely be dereferenced.  Otherwise it depends on
the specific circumstances.  Simply converting a pointer does not
in itself impose any sanity requirements on the resulting pointer.



Wed, 19 May 1993 14:38:00 GMT  
 
 [ 2 post ] 

 Relevant Pages 

1. LINT or similar program?

2. Lint question

3. Lint questions

4. are questions about lint topical?

5. Lint question

6. lint question - how to "import" VARARGS

7. 2 lint questions

8. lint question

9. A lint question

10. Question on shutting up lint

11. lint question

12. Simple lint(1) question.

 

 
Powered by phpBB® Forum Software