Re: warning: dereferencing `void *' pointer

From: Chris Torek (nospam_at_torek.net)
Date: 12/14/03

  • Next message: Simon Biber: "Re: undefined reference to `OpenPrinterA@12'"
    Date: 14 Dec 2003 04:45:28 GMT
    
    

    In article <3FDBD7E0.7040607@gri.msstate.edu>
    Pushkar Pradhan <pushkar@gri.msstate.edu> writes:
    >I have some functions which take as i/p a buffer (it can be float, char,
    >or 16 bit, int etc.). The result is another o/p buffer, its type is also
    >flexible (it could be a float, char etc.).
    >
    >I try to pass both as "void *buf" so that it can accept any data type.
    >But since I access the buffer and try to assign its elements to another
    >I get compile errors (I have pasted at the end).
    >
    >Now my question is how can I pass the i/p and o/p buffers as args to the
    >function without hardcoding the data type, someone earlier suggested to
    >pass another argument which tells the type, e.g.
    >myfunction(void * buf, 1, ...) 1 may indicate that buf is char type, 2
    >means 16 bit etc.
    >
    >But that means having if .. else's inside the function to do the same
    >thing. Is there any other way?

    Here is a bit pattern, 32 bits long:

        11000101001110101001110101000110

    What does it mean? What value(s) does it represent?

    Does your answer change if I tell you it is the raw bits of a 32-bit
    "float"? What if I say it is a 32-bit VAX float, or perhaps a 32-bit
    SPARC float? Suppose I say it is, instead, a 32-bit "int", or a
    32-bit "signed int". Now what value(s) can it represent? What if
    it is an array of four "char"s or four "unsigned char"s?

    (Finding answers -- there may be more than one -- to the above
    questions is a good and useful exercise, that will help you understand
    how the system(s) you use interpret bits. Note that different
    systems interpret the same bits differently! One reason we use
    "high level" source code -- although C is relatively low level for
    a high level language -- is to avoid having to specify the precise
    set of bits, which change from system to system, when we can get
    away with specifying only what we want the bits to mean. The
    source-code number "3.1415" or "42" has a definite meaning, and
    the fact that machines A and B use different bit-patterns to
    represent them is -- we hope -- irrelevant.)

    Note that all of these are 32 bits long. They all look exactly the
    same when treated as a 32-bit-long bit-string. But they all represent
    different values.

    When you use "void *" in C, you are -- usually -- implicitly using
    a "bag of uninterpreted bits" type. Any interpretation necessarily
    arises from giving the "bag of bits" some more-specific type. You
    must decide whether you intend to interpret the bits, and if so, how.
    This will dictate the type(s) you must provide.

    Note that a series of if/else's, or a switch, inside a function
    that receives a "void *" but wants to interpret the bits as one
    of (say) the two possibilities "int" and "float" will result in C
    code looking like:

        enum whichtype { TY_INT, TY_FLOAT };
        /*
         * Here p points to the first bit(s) in the bag-o-bits, "len"
         * is the number of items of some interpreted type, and "ty"
         * is the enumeration saying which type.
         */
        void f(void *p, size_t len, enum whichtype ty) {
            size_t i;
            int *ip;
            float *fp;

            switch (ty) {
            case TY_INT:
                ip = p;
                ... work with ip[i], where i is between 0 and len-1, e.g:
                for (i = 0; i < len; i++)
                    ip[i] *= 2;
                break;

            case TY_FLOAT:
                fp = p;
                ... work with fp[i], where i is between 0 and len-1 ...
                for (i = 0; i < len; i++)
                    fp[i] *= 2.0;
                break;

            default:
                panic("invalid type argument %d to f()", (int)ty);
                /* NOTREACHED */
            }
        }

    Not only do you really, truly have to write the if/else or switch,
    you will also find that, on typical machines, it compiles to code
    that uses different machine instructions to manipulate the data.
    The code using ip[i] might look like, e.g.:

            # this doubles ip[i], where i is in %eax and ip is in %edx
            movl (%edx,%eax,4),%ecx
            addl %ecx,%ecx
            movl %ecx,(%edx,%eax,4)

    versus:

            # this doubles fp[i], where i is in %eax and fp is in %edx
            flds (%edx,%eax,4)
            fadds (%edx,%eax,4)
            fstps (%edx,%eax,4)

    These completely-different machine instructions are utterly necessary
    here -- using the "addl %ecx,%ecx" instructions will not result in
    doubling a "float" value in fp[i], but rather will just make a mess
    of the value.

    Thus, different C code is required because different machine code
    is required. Using "void *" absolves neither you nor the compiler
    of this.

    If you do enough of this sort of thing (or analyse your systems
    carefully), you will find that there are cases where the C source
    code must be different, yet the underlying machine code on your
    machine(s) is the same. This happens because (and when) the abstract
    meaning defined in C for two different types happens to be implemented
    using a single underlying machine-level mechanism. But in general,
    the C code should remain separate, because on some *other* machine(s),
    this may no longer be the case. In other words, in the C code,
    you write *what* you want to have happen, not *how* to *make* it
    happen. If you want different things, at the C level, that all
    happen to be done the same way at the machine level, it is the
    compiler's job to discover this and optimize the code appropriately.
    (Some compilers are better than others at this, and you may sometimes
    find yourself having to "help" some compilers a bit. Do this only
    when you really know what you are doing. :-) )

    -- 
    In-Real-Life: Chris Torek, Wind River Systems
    Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W)  +1 801 277 2603
    email: forget about it   http://web.torek.net/torek/index.html
    Reading email is like searching for food in the garbage, thanks to spammers.
    

  • Next message: Simon Biber: "Re: undefined reference to `OpenPrinterA@12'"

    Relevant Pages

    • Re: demonic problem descriptions
      ... And the context for Lisp should be compatible with human ... default float interpretation is not compatible with human ... > interpret the notation of tokens as floating point numbers. ...
      (comp.lang.lisp)
    • Re: State of Forth 200x
      ... not having a switch to tell the system to interpret every number ... as a float. ... just don't like that the system crashes now and then and that it ...
      (comp.lang.forth)
    • Re: VertexDeclaration format into VS, dx9
      ... I think what people usually do is declare it as FLOAT1, but interpret ... it as a float in the shader. ...
      (microsoft.public.win32.programmer.directx.graphics)
    • Re: the round() functions seems to be totally broken
      ... Lew Pitcher said on 1/19/2004 4:52: ... > to interpret the return value from roundas a float, ... I guess a trip to the *printf man pages would be in order;-) ...
      (alt.os.linux)
    • Re: the round() functions seems to be totally broken
      ... Lew Pitcher said on 1/19/2004 4:52: ... > to interpret the return value from roundas a float, ... I guess a trip to the *printf man pages would be in order;-) ...
      (comp.os.linux.misc)