Passing different variable types to a function

Discussion in 'C Programming' started by Shawn, Jan 16, 2006.

  1. Shawn

    Shawn Guest

    As if it won't be clear enough from my code, I'm pretty new to C
    programming. This code is being compiled with an ANSI-C compatible
    compiler for a microcontroller. That part, I believe, will be
    irrelavent. My syntax is surely where I am going wrong.

    I'd like to be able to call this routine to read different values from
    another device. This routine would be called quite simply as follows:

    void main()
    {
    long test;
    read7753(test,3);
    }

    "test" may be a char, int, unsigned int, long, or unsigned long. My
    problem is in figuring out how best to pass the variable that will
    receive the value once it is read. That is, how can I get "data" from
    my function below back into whatever variable I called it from, e.g.
    "test" no matter what type of variable "test" is?

    Thanks all, very much for any assistance.




    void read7753(unsigned long data, int nbrBytes)
    {
    unsigned char i;

    // Shift bits from the ADE7753.
    BitWrPortI(PBDR, &PBDRShadow, 0, 7); //CS Low

    for (i = 0; i < nbrBytes*8; ++i) {
    BitWrPortI (PFDR, &PFDRShadow, 0, 1);
    BitWrPortI (PFDR, &PFDRShadow, 1, 1);

    data = data << 1;
    data |= BitRdPortI (PCDR, 2);
    } // END for

    BitWrPortI(PBDR, &PBDRShadow, 1, 7); //CS High

    }
     
    Shawn, Jan 16, 2006
    #1
    1. Advertising

  2. Shawn

    Chris Torek Guest

    In article <>,
    Shawn <> wrote:
    >As if it won't be clear enough from my code, I'm pretty new to C
    >programming. This code is being compiled with an ANSI-C compatible
    >compiler for a microcontroller. That part, I believe, will be
    >irrelevent.


    Maybe, maybe not. A lot of compilers for particularly-tiny
    microcontrollers fail to actually implement ANSI/ISO C, even
    in its "freestanding" form.

    This newsgroup (comp.lang.c) is mostly dedicated to hosted
    compilers, since a lot of things are unpredictable in freestanding
    systems, including the type of main(), and whether there is
    even a main() at all. That said, however...:

    >I'd like to be able to call this routine to read different values from
    >another device. This routine would be called quite simply as follows:
    >
    >void main()
    >{


    If this were a hosted system, main() would have to have return-type
    "int". I suspect this *is* a freestanding system, though, in which
    case perhaps this function ought to be named "start" or some such.
    Not that it matters too much, because:

    > long test;
    > read7753(test,3);


    this is unlikely to do anything useful. Here "test" is an
    uninitialized "auto"-storage-class variable (i.e., not "static"
    so not initialized to zero for you). It contains garbage. You
    then pass its value to read7753(), because C always passes all
    arguments by value.

    >}
    >
    >"test" may be a char, int, unsigned int, long, or unsigned long. My
    >problem is in figuring out how best to pass the variable that will
    >receive the value once it is read. That is, how can I get "data" from
    >my function below back into whatever variable I called it from, e.g.
    >"test" no matter what type of variable "test" is?


    C always passes all arguments by value. In order to change an object
    (loosely, "a variable"), you must pass, as a value, a pointer that
    points to some part(s) of that object. The simplest thing, in most
    but not all cases, is to pass a pointer to the entire object.

    In other words, you do not pass "the object" at all: instead, you
    pass "the address of the object". In C, you might write this as:

    read7753(&test, 3);

    (This makes the rather brash assumption that "test" is made up of
    three bytes, in this particular case, but since you are programming
    for an embedded microcontroller, you might actually know that it
    is indeed a 3-byte, or 24-bit, object, because your platform uses
    3 8-bit bytes for a "long". Note, however, that using only 3 8-bit
    bytes for a "long" fails to conform to the ANSI/ISO requirements,
    even for freestanding compilers, where a "long" must be able to
    hold values in the range [-2147483647..+2147483647]. Yes, both of
    those numbers are the same -- ones' complement and sign-and-
    magnitude representations are permitted. To obtain that range, at
    least 32 bits are required, and the only way to make that work with
    as few as three bytes is to have at least 11 bits per byte. 11
    bits per byte is allowed -- any number from 8 on up is OK -- but
    some of your code assumes 8 bits per byte, which is certainly the
    most common case today.)

    The problem with simply using "&test" here is that &test has type
    "pointer to long", or "long *". This would be fine if you were
    only ever going to read into "long"s, but if you are going to read
    into variables of other types, the read7753() function cannot be
    restricted to writing only "long"s.

    The solutions (there are many but only two are "reasonable", and
    only one is "obviously best" -- for some definitions of obvious
    and best -- provided your compiler is in fact ANSI/ISO conformant)
    are to use the fact that it is not necessary to point to the
    *entire* object. It suffices to point to the "first byte" of the
    object. In C, all objects must be "break-up-able" into individual
    bytes, which C calls "char"s. You can use a pointer to char --
    preferably a pointer to "unsigned char", which avoids the possibility
    of trap representations (which your implementation no doubt does
    not have anyway, but bear with me) -- to point to the various
    bytes, as in:

    unsigned char *p;
    long somevar;
    p = (unsigned char *)&somevar;

    After this, you can refer to p[0], p[1], p[2], and so on, to access
    the individual bytes making up "somevar". The maximum legal
    subscript is (sizeof(long)-1), typically 3 or 7 on most machines
    today (the value will depend on the compiler and machine).

    Suppose, then, that sizeof(long)==4 and read7753() will fill in
    the given number of C bytes (as "unsigned char"s). Then we can
    do this:

    long test = 0;

    read7753((unsigned char *)&test, 3);

    The reason for initializing "test" to zero is that read7753() will
    only *write* three bytes, but -- per our assumptions above -- the
    "long" is made up of four bytes. We initialize all of them, and
    then allow read7753() to overwrite three of the four. The remaining
    one remains zero, which is likely to be helpful when we go to do
    something with the value now in "test". (Or it might not be
    useful, but in that case, why bother with all this, when we could
    write instead:

    unsigned char buf[3];

    read7753(&buf[0], 3);

    and fill in just the three bytes in "buf", with no wasted fourth
    byte? Presumably the point of assembling all the bits together
    into a "long" was to do some sort of arithmetic on them.)

    This method is likely to work on all compilers that claim to
    implement the C language, even if what they actually implement
    is something almost but not entirely unlike C. If the compiler
    really, actually does implement C, however, there is a "better"
    way.

    Note that, except when passing the address of some "unsigned char"
    object (including the address of the first element of an array of
    unsigned char), the call to read7753() above requires a cast. In
    C, the presence of a cast does two things:

    - instructs the compiler to convert a value of some type to
    a new value of some other type (the new type being the one
    in parentheses, and a parenthesized type-name constituting
    a cast); and

    - tells the compiler that, no matter how dubious that
    conversion may be, to please shut the he{ck | ll} up about it
    and just do it, even if it is clearly, obviously, 100% wrong,
    because the programmer knows better.

    The first one is all well and good, but the second one introduces
    all kinds of opportunities for error. For this reason, it is a
    good idea to try to avoid using casts whenever possible -- and
    ANSI/ISO C allows avoiding the cast by using "void *".

    When you write the prototype for the read7753() function -- including
    the one that you will write while defining the function -- you can
    give the first argument the type "void *". Note that the memcpy()
    and memset() functions make use of "void *", for the same reason.
    Thus:

    #include <stdlib.h> /* for size_t */

    void read7753(void *, size_t);

    int main(void) {
    long test = 0;

    read7753(&test, 3); /* XXX assumes sizeof test >= 3 */
    ... do something with "test" ...
    }

    void read7753(void *data0, size_t nbytes) {
    unsigned char *data = data0;
    ... do stuff to fill in data ...
    }

    While "void *" has been standard since 1990 (the 1989 C standard
    was approved in late December 1989, late enough to really be a 1990
    thing), a lot of freestanding microprocessor "C compilers" never
    got around to implementing a lot of the 1989 standard, much less
    the new 1999 standard. So there are some on which "void *" may
    not work right.

    I will leave the code for working (one bit at a time) with the
    "ADE7753" (whatever that is) to someone else, but suffice it to
    say that if you are going to read some (variable) number of bytes
    from it, you may want to do that one byte ("unsigned char" in C)
    at a time.
    --
    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.
     
    Chris Torek, Jan 16, 2006
    #2
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    8
    Views:
    573
    PJP of NYC
    May 24, 2005
  2. Kiran
    Replies:
    6
    Views:
    424
    Christopher Benson-Manica
    Jan 18, 2007
  3. __PaTeR
    Replies:
    7
    Views:
    499
    Barry Schwarz
    Jan 1, 2009
  4. manu
    Replies:
    5
    Views:
    401
    Nick Keighley
    Sep 17, 2009
  5. ittium
    Replies:
    4
    Views:
    329
    Goran
    Dec 9, 2011
Loading...

Share This Page