Passing arg from incompatible pointer type

Discussion in 'C Programming' started by Chris Readle, Jun 20, 2004.

  1. Chris Readle

    Chris Readle Guest

    Hi all,

    Somewhat new to C and I'm getting the following error from my latest code.

    Here's the warning I'm getting:
    chris_readle_project3_assignment3.c: In function `main':
    chris_readle_project3_assignment3.c:23: warning: passing arg 1 of
    `displaySales' from incompatible pointer type

    And here is the code in question:
    void inputSales(float s[][PRODUCTS]);
    void displaySales(const float s[][PRODUCTS]);

    float sales[SALESPEOPLE][PRODUCTS] = {{0}, {0}, {0}, {0}};

    inputSales(sales);

    displaySales(sales); <---------This one gets the error

    So, why am I getting the error and what does it mean? And why do I get
    it on the second function call and not the first, which seems exactly
    the same, other than being to a different function and not declaring the
    parameter as a const?

    crr
     
    Chris Readle, Jun 20, 2004
    #1
    1. Advertisements

  2. Chris Readle

    xarax Guest

    That's the answer. The parameter is declared const
    and the passed value is not a const.
     
    xarax, Jun 21, 2004
    #2
    1. Advertisements

  3. Chris Readle

    Chris Readle Guest

    Aha, that's what I suspected.

    So this is not any kind of hidden thing that's going to come back and
    bite me later? I had noticed that everything seems to run just fine,
    but I wanted to make sure that this wasn't something that *could* mess
    me up later and also that it's not some big style faux pas of which I
    was unaware.

    crr
     
    Chris Readle, Jun 21, 2004
    #3
  4. Chris Readle

    Old Wolf Guest

    You can't "do away with const" over 2 levels of indirection. If they
    were 1-D arrays it would have been OK. This is touched on in FAQ 11.10.
    There's a good reason for this rule (although it escapes me at the moment).
     
    Old Wolf, Jun 21, 2004
    #4
  5. Chris Readle

    S.Tobias Guest

    I think that's an interesting problem.

    float *pf;
    const float **ppcf = &pf; //unsafe, we are lying to ppcf about pf
    const float fdata;
    *ppcf = &fdata; //legal, pf is pointed to fdata,
    //ppcf "thinks" it is safe
    *pf = 13; //disaster!

    In general, we were lying as to kind of an object pf points to.


    But in the original problem we deal with tables and there is
    only *one* level of indirection: `sales' decays to:
    float (*)[PRODUCTS]
    and is passed to `s', which decays to:
    const float (*)[PRODUCTS]
    (All items in the table are const, so we might say that the
    whole "table object" is const.)
    It is like passing `float*' to `const float*', with exception
    that in this case `float' is actually `float[PRODUCTS]' (or put
    a structure there with PRODUCTS members of type float).

    This even looks more like a C++ question.
    Can anyone comment on this?
     
    S.Tobias, Jun 21, 2004
    #5
  6. Chris Readle

    Dan Pop Guest

    Staying away from "const" helps preserve your sanity (and your hair).

    Dan
     
    Dan Pop, Jun 21, 2004
    #6
  7. Chris Readle

    Chris Readle Guest

    Dan Pop wrote:
    -snip-
    Is there another way to simulate least privilege here? This is a
    display function that should never modify the data in question, but
    *does* need to read it? Or should I just leave it modifiable and make
    in clear in comments (not that this code will ever be seen by anyone but
    me and my instructor, but I'm speaking more generally here) that to
    modify the table in displaySales will bring a slow and painful death?

    crr
     
    Chris Readle, Jun 21, 2004
    #7
  8. Chris Readle

    S.Tobias Guest

    Thanks for advice, a few years too late, though. As for my hair - soon
    there might be no issue :)


    I have run OP's code through on-line como: error (warning) in C99
    and C90 mode in strict (relaxed) mode, and no diagnostics in C++ mode.
    Similarly with gcc/g++.

    In C99 Std 6.5.16.1 (Simple assignment): "[...] type pointed to by the
    left has all the qualifiers of the type pointed to by the right;",
    and argument passing relies on assignment for that matter (6.5.2.2).

    In C++ the right expression is converted to the type of left one,
    which includes Qualification conversion (4.4), which is slightly
    more complicated and more logical than in C IMO.

    To sum up: C (unnecessarily) requires all pointed to cv-quals the
    same and there's no way without a cast - even if we drop const in
    fn parameter, we might want to pass `const float[][]' and run into
    same problem - unless we "stay away from const" completely.
    (There you are, I've almost come to the same conclusion...)
     
    S.Tobias, Jun 21, 2004
    #8
  9. Chris Readle

    S.Tobias Guest

    My advice would be to keep it as it is, and cast the argument, and
    document why the cast was made:
    void displaySales(const float s[][PRODUCTS]);
    void test()
    {
    displaySales( (const float (*)[PRODUCTS]) sales); /*cast in const*/
    }


    My personal way of _documenting_ this is a la C++ (though its meaning and
    usage is *completely different*):
    #define const_cast(type) (type)
    displaySales( const_cast(const float (*)[PRODUCTS]) sales);
    But some people don't like it.
     
    S.Tobias, Jun 21, 2004
    #9
  10. Chris Readle

    Chris Readle Guest

    S.Tobias wrote:

    -snip a bunch of my crap-
    -snip-

    First, thanks for all your help.

    Ok, let me see if I understand this, the call:
    displaySales((const float (*)[PRODUCTS]) sales); is basically saying:

    "Call the function displaySales with the array sales cast as a pointer
    to a const of type float." Is that correct?

    I may be dense (ok, so no "may" about it ;) ), but what is the
    [PRODUCTS] doing inside the cast?

    crr
     
    Chris Readle, Jun 21, 2004
    #10
  11. Chris Readle

    S.Tobias Guest

    Err, no...
    It's a bit long story.

    First, learn to read type definitions (this one is a little complex,
    but not the most difficult one). There was this nice rule: follow
    the parentheses, otherwise first read what is to the right, then
    to the left.
    const float (*)[PRODUCTS]
    1. pointer
    2. to length PRODUCTS table
    3. of const float values
    (in short: "pointer to table of floats")
    I'd advise you to look into 6.7.6#3 of C99 Standard and see
    a couple of fine examples.

    Now, why "[]":
    I'm sure you know that ptab2 and ptab3 (pointers to table of int) in:
    int (*ptab2)[2];
    int (*ptab3)[3];
    are incompatible - the pointer arithmetic is different.
    (If you don't see why, imagine:
    typedef struct {int i0; int i1; } stab2_t;
    typedef struct {int i0; int i1; int i2; } stab3_t;
    stab2_t *pstab2;
    stab3_t *pstab3;
    Types stab2_t and stab3_t are different sizes; so are above "inner" tables.)
    Compiler has to know the length of "inner" tables in order to know
    the proper pointer arithmetic (hence the actual pointer type), but needn't
    know the "outer" length. Thats why you can declare:
    void inputSales(float s[SALESPEOPLE][PRODUCTS]); /*SALESPEOPLE is ignored*/
    or
    void inputSales(float s[][PRODUCTS]);
    and you can pass arguments:
    float sales[SALESPEOPLE][PRODUCTS];
    or
    float sales2[SALESPEOPLE+20][PRODUCTS];
    but you can't pass:
    float sales3[SALESPEOPLE][PRODUCTS+1];
    ^ inner table length is different


    Second, why this _form_ of cast:

    `sales' is a table:
    float sales[][PRODUCTS]; /*length doesn't matter*/
    but parameter `s' is:
    const float s[][PRODUCTS];

    So when passing `sales' to `displaySales' we need to cast it to type like:
    const float [][PRODUCTS]; /*table of: length PRODUCTS table of: const float*/
    But in a cast operator type name shall be of "scalar type and the operand
    shall have scalar type" (6.5.4#2). `sales' in the expression is
    converted (decays) to "pointer to table of float" (6.3.2.1#3), so it is
    the right (scalar) type here, and its type is:
    float (*)[PRODUCTS]
    What I did was just add `const' before `float' to convert it to
    the desired type.

    (Sorry, could have made it shorter, but I don't want
    to change all this at this point now.)
     
    S.Tobias, Jun 22, 2004
    #11
  12. Chris Readle

    Chris Readle Guest

    S.Tobias wrote:

    Again, thanks for the help.
    Aha, that's what I was missing. I thought it had something to do with
    the length of the table, but I wasn't sure how that fit in. I just
    finished DLing the Standard, and I'll look up the section you suggest.
    -snip-
    No, this was perfect (for me, at least) it really helped me understand
    what was going on the cast.

    Yet again, thanks for all the help, you're a life saver (or at least a
    sanity saver ;) ).

    crr
     
    Chris Readle, Jun 22, 2004
    #12
  13. Chris Readle

    Dan Pop Guest

    It is already implied by the name and semantics of the function.

    Only when a display function *has* to modify its input data is a special
    comment necessary, the default assumption is that such a function only
    examines its input data.

    The only "valid" argument for using const in such cases I have seen
    until now is "I am an idiot and I cannot trust myself not to accidentally
    alter the data, so I *need* the compiler diagnostics".

    Dan
     
    Dan Pop, Jun 22, 2004
    #13
  14. Chris Readle

    Old Wolf Guest

    Or a slight variation: "Other developers maintaining this code or using
    this library in their own code might be idiots, and I do not want
    to waste my time fielding calls and emails from idiots who crash their
    program and think it's my fault".
     
    Old Wolf, Jun 23, 2004
    #14
  15. Chris Readle

    CBFalconer Guest

    Or, 3, 6, 12, whatever months from now I or someone else may be
    modifying this code without taking the time to read and absorb the
    mountainous documentation (if any). It would be nice to have a
    mistake exposed at the earliest possible moment.

    Believe it or not, computers (even some of those with Microsoft
    software) are usually better at remembering and applying minutiae
    than you are. I am not too proud to accept help.
     
    CBFalconer, Jun 23, 2004
    #15
  16. Chris Readle

    Dan Pop Guest

    You don't need to study the documentation, in order to change the code.
    Reading the code itself should be enough.
    If you don't know what you're doing, you have no business touching that
    code. There are far too many ways in which you can break it and no
    compiler could possibly expose your mistakes.

    I have no problems maintaining code I wrote 10 years ago, despite the
    complete lack of const in it. And, by engaging my brain, I've been also
    maintaining code (more or less badly) written by other people, without
    relying on the compiler to find anything else than my own typos.
    Computers can't think for you. If you can't think, either, find a
    different kind of job.

    Dan
     
    Dan Pop, Jun 24, 2004
    #16
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.