is const necessary in eg int compar(const void *, const void *)

Discussion in 'C Programming' started by lovecreatesbeauty@gmail.c0m, Oct 15, 2008.

  1. 0m

    0m Guest

    Is the keyword const necessary in the comparison function in qsort and
    bsearch?

    int (*compar)(const void *, const void *)

    If the pointer cannot be dereferenced why worry if the pointed object
    will be modified?
    0m, Oct 15, 2008
    #1
    1. Advertising

  2. 0m

    Ian Collins Guest

    Re: is const necessary in eg int compar(const void *, const void*)

    0m wrote:
    > Is the keyword const necessary in the comparison function in qsort and
    > bsearch?
    >
    > int (*compar)(const void *, const void *)
    >
    > If the pointer cannot be dereferenced why worry if the pointed object
    > will be modified?


    Why can't the pointer be dereferenced?

    --
    Ian Collins
    Ian Collins, Oct 15, 2008
    #2
    1. Advertising

  3. "0m" <> wrote:
    > Is the keyword const necessary in the comparison function
    > in qsort and bsearch?


    Not strictly necessary, but prudent.

    > int (*compar)(const void *, const void *)
    >
    > If the pointer cannot be dereferenced why worry if the
    > pointed object will be modified?


    When the pointer(s) are converted to the appropriate object
    type, they certainly can be dereferenced, and usually are.

    --
    Peter
    Peter Nilsson, Oct 15, 2008
    #3
  4. 0m

    0m Guest

    On Oct 15, 10:55 am, Peter Nilsson <> wrote:
    > "0m" <> wrote:
    > > Is the keyword const necessary in the comparison function
    > > in qsort and bsearch?

    >
    > Not strictly necessary, but prudent.
    >
    > > int (*compar)(const void *, const void *)

    >
    > > If the pointer cannot be dereferenced why worry if the
    > > pointed object will be modified?

    >
    > When the pointer(s) are converted to the appropriate object
    > type, they certainly can be dereferenced, and usually are.
    >


    Thank you.

    I ignored this, the const qualifier on the original pointers will
    still require the same qulifier to be applied on those appropriate
    object.
    0m, Oct 15, 2008
    #4
  5. 0m

    0m Guest

    On Oct 15, 11:16 am, Richard Heathfield <> wrote:
    > 0m said:
    > > If the pointer cannot be dereferenced why worry if the pointed object
    > > will be modified?

    >
    > The pointer *can* be dereferenced after conversion to the appropriate type.
    > For example, look at all the dereferencing going on here:
    >


    Yes, thanks. I got it.

    >   if(0 == diff)
    >   {
    >     diff =
    >     (left->tm_mon > right->tm_mon) - (left->tm_mon < right->tm_mon);
    >   }
    >   if(0 == diff)
    >   {
    >     diff =
    >     (left->tm_mday > right->tm_mday) - (left->tm_mday < right->tm_mday);
    >   }
    >


    why don't you put your code together and create another same selection
    block :)
    0m, Oct 15, 2008
    #5
  6. "0m" <> writes:

    > On Oct 15, 10:55 am, Peter Nilsson <> wrote:
    >> "0m" <> wrote:
    >> > Is the keyword const necessary in the comparison function
    >> > in qsort and bsearch?

    >>
    >> Not strictly necessary, but prudent.
    >>
    >> > int (*compar)(const void *, const void *)

    >>
    >> > If the pointer cannot be dereferenced why worry if the
    >> > pointed object will be modified?

    >>
    >> When the pointer(s) are converted to the appropriate object
    >> type, they certainly can be dereferenced, and usually are.
    >>

    >
    > Thank you.
    >
    > I ignored this, the const qualifier on the original pointers will
    > still require the same qulifier to be applied on those appropriate
    > object.


    Hmm, but wait a minute. I think your original point was valid.

    For a function like

    void foo(const int *p) { /* ... */ }

    the compiler will complain if you attempt to assign to, or otherwise
    modify, *p. So the compiler helps you make good on the promise you're
    making to the caller, that *p will not be modified; this is the whole
    point of const. You could circumvent this by assigning to *(int *)p,
    but at least the fact that you had to introduce a cast serves as warning
    that you might be doing something wrong.

    But for a function like

    void bar(const void *p) { /* ... */ }

    where p is known to point to an int, you *have* to cast p before you can
    do much with it. And if you're going to cast it, you could just as
    easily cast it to `int *' as to `const int *', and the compiler will (in
    my tests) remain silent either way. So the help you get from using
    const is much less.

    Now if we also have

    void qux(void *);

    and `bar' attempts to call `qux(p)', the compiler does whine. (This in
    fact is exactly the whine that occurs if you pass a function taking
    `void *' arguments to `qsort'.) But certainly a lot of the protection
    has been lost. One could argue that this makes the use of
    `const void *' sort of pointless, but if nothing else it provides some
    documentation, I suppose.
    Nate Eldredge, Oct 15, 2008
    #6
  7. Richard Heathfield <> writes:

    > Nate Eldredge said:
    >
    > <snip>
    >
    >> But for a function like
    >>
    >> void bar(const void *p) { /* ... */ }
    >>
    >> where p is known to point to an int, you *have* to cast p before you can
    >> do much with it.

    >
    > Not so:
    >
    > #include <stdio.h>
    >
    > void bar(const void *p)
    > {
    > const int *pi = p;
    > printf("%d\n", *pi);
    > } /* look, ma, no casts */


    Aha. Whereas assigning p to a variable of type `int *' does result in a
    diagnostic. Got it.

    So therefore it would be good practice in functions accepting `const
    void *' arguments to assign to a temporary pointer of the appropriate
    type, rather than casting, in order to preserve the usefulness of
    const. Your function is thus to be preferred to

    void bar(const void *p)
    {
    printf("%d\n", *(const int *)p);
    }

    since the latter habit, in a larger function, might let you
    inadvertently omit const from the cast, and perhaps modify *p.
    Nate Eldredge, Oct 15, 2008
    #7
  8. 0m

    James Kuyper Guest

    Re: is const necessary in eg int compar(const void *, const void*)

    0m wrote:
    > On Oct 15, 11:16 am, Richard Heathfield <> wrote:
    >> 0m said:
    >>> If the pointer cannot be dereferenced why worry if the pointed object
    >>> will be modified?

    >> The pointer *can* be dereferenced after conversion to the appropriate type.
    >> For example, look at all the dereferencing going on here:
    >>

    >
    > Yes, thanks. I got it.
    >
    >> if(0 == diff)
    >> {
    >> diff =
    >> (left->tm_mon > right->tm_mon) - (left->tm_mon < right->tm_mon);
    >> }
    >> if(0 == diff)
    >> {
    >> diff =
    >> (left->tm_mday > right->tm_mday) - (left->tm_mday < right->tm_mday);
    >> }
    >>

    >
    > why don't you put your code together and create another same selection
    > block :)


    If the value of diff was 0 at the first if(), then diff gets
    recalculated, in which case it might be non-0 for the second if(), so
    you can't combine the two blocks into a single if() block.
    James Kuyper, Oct 15, 2008
    #8
  9. On 15 Oct 2008 at 8:45, Richard Heathfield wrote:
    > I'm of the opinion that it's always possible, and usually preferable,
    > to avoid casting.


    As usual, your blind dogmatism leads you to talk nonsense.

    A couple of trivial examples that come quickly to mind:

    1) When passing a NULL pointer to variadic functions, you need an
    explicit cast, e.g.
    s = mystrconcat("hello", " ", "world", "!", (char *) NULL);

    2) If you have a type that you know is an integer type but is typedef'd
    in a system-specific header, then to portably print it you need a cast
    to the widest integer type:

    opaque_integer_type i = somefunction(42);
    printf("The answer is %llu\n", (unsigned long long) i);
    Antoninus Twink, Oct 15, 2008
    #9
  10. In article <>,
    Antoninus Twink <> wrote:

    >1) When passing a NULL pointer to variadic functions, you need an
    >explicit cast, e.g.
    >s = mystrconcat("hello", " ", "world", "!", (char *) NULL);


    I'm sure someone will point out how it *can* be done without a cast,
    but of course a cast is the natural way to do it.

    >2) If you have a type that you know is an integer type but is typedef'd
    >in a system-specific header, then to portably print it you need a cast
    >to the widest integer type:
    >
    >opaque_integer_type i = somefunction(42);
    >printf("The answer is %llu\n", (unsigned long long) i);


    Alternatively the implementor can provide a macro for a format string,
    as C99 does (PRIdMAX etc).

    -- Richard



    --
    Please remember to mention me / in tapes you leave behind.
    Richard Tobin, Oct 15, 2008
    #10
  11. Richard Heathfield <> writes:

    > Richard Tobin said:
    >
    >> In article <>,
    >> Antoninus Twink <> wrote:
    >>
    >>>1) When passing a NULL pointer to variadic functions, you need an
    >>>explicit cast, e.g.
    >>>s = mystrconcat("hello", " ", "world", "!", (char *) NULL);

    >>
    >> I'm sure someone will point out how it *can* be done without a cast,

    >
    > Mr Twink is wrong on technical grounds and sub-optimal on stylistic
    > grounds: firstly, you *don't* need a cast, and secondly, "explicit" is
    > redundant.


    You're thinking of something like

    char *n = NULL;
    s = mystrconcat("hello", " ", "world", "!", n);

    I assume?

    >> but of course a cast is the natural way to do it.

    >
    > Yes. I don't think anyone has claimed otherwise.
    >
    >>>2) If you have a type that you know is an integer type but is typedef'd
    >>>in a system-specific header, then to portably print it you need a cast
    >>>to the widest integer type:
    >>>
    >>>opaque_integer_type i = somefunction(42);
    >>>printf("The answer is %llu\n", (unsigned long long) i);

    >>
    >> Alternatively the implementor can provide a macro for a format string,
    >> as C99 does (PRIdMAX etc).

    >
    > Yes. But if the type is opaque and not supported by the implementation, the
    > cast is in any case unwise. Instead, you should pass the value to the
    > opaque type's stream-writing function.


    It's a pleasant world you live in where such things always exist. :)

    Unix, for instance, is rife with integer types that don't come with
    output functions. pid_t, uid_t, half the fields of struct stat, etc.
    So I tend to do

    printf("The file has inode number %ju\n", (uintmax_t)st.st_ino);

    (st.st_ino has type `ino_t').

    Of course, I can avoid the cast by saying

    uintmax_t inode = st.st_ino;
    printf("The file has inode number %ju\n", inode);
    Nate Eldredge, Oct 15, 2008
    #11
  12. 0m

    0m Guest

    On Oct 15, 6:21 pm, James Kuyper <> wrote:
    > 0m wrote:
    > > On Oct 15, 11:16 am, Richard Heathfield <> wrote:
    > >> 0m said:
    > >>> If the pointer cannot be dereferenced why worry if the pointed object
    > >>> will be modified?
    > >> The pointer *can* be dereferenced after conversion to the appropriate type.
    > >> For example, look at all the dereferencing going on here:

    >
    > > Yes, thanks. I got it.

    >
    > >> if(0 == diff)
    > >> {
    > >> diff =
    > >> (left->tm_mon > right->tm_mon) - (left->tm_mon < right->tm_mon);
    > >> }
    > >> if(0 == diff)
    > >> {
    > >> diff =
    > >> (left->tm_mday > right->tm_mday) - (left->tm_mday < right->tm_mday);
    > >> }

    >
    > > why don't you put your code together and create another same selection
    > > block :)

    >
    > If the value of diff was 0 at the first if(), then diff gets
    > recalculated, in which case it might be non-0 for the second if(), so
    > you can't combine the two blocks into a single if() block.- Hide quoted text -
    >


    Yes, but I don't think the original code is a good piece.

    #include <time.h>

    int cmp_tm(const void *v1, const void *v2)
    {
    const struct tm *t1 = v1;
    const struct tm *t2 = v2;
    int diff_y, diff_m, diff_d;

    diff_y = t1->tm_year != t2->tm_year;
    diff_m = t1->tm_mon != t2->tm_mon;
    diff_d = t1->tm_mday != t2->tm_mday;

    if (!diff_y && !diff_m){
    /* usage of diff_d; */
    }

    /* hour, min, sec similarly */

    return diff_d; /*diff in sec maybe*/
    }
    0m, Oct 15, 2008
    #12
  13. 0m

    James Kuyper Guest

    Re: is const necessary in eg int compar(const void *, const void*)

    0m wrote:
    > On Oct 15, 6:21 pm, James Kuyper <> wrote:

    ....
    > Yes, but I don't think the original code is a good piece.
    >
    >
    >
    > #include <time.h>
    >
    > int cmp_tm(const void *v1, const void *v2)
    > {
    > const struct tm *t1 = v1;
    > const struct tm *t2 = v2;
    > int diff_y, diff_m, diff_d;
    >
    > diff_y = t1->tm_year != t2->tm_year;
    > diff_m = t1->tm_mon != t2->tm_mon;
    > diff_d = t1->tm_mday != t2->tm_mday;
    >
    > if (!diff_y && !diff_m){
    > /* usage of diff_d; */
    > }
    >
    > /* hour, min, sec similarly */
    >
    > return diff_d; /*diff in sec maybe*/
    > }


    A comparison function intended for use by qsort() or bsearch() must
    return a result that is either positive, zero, or negative, depending
    upon the relative order of the two arguments. Your function always
    returns a value of 1 if the two arguments are not equal; it never
    returns a negative value.

    Your code doesn't seem to handle the cases where diff_y!=0, or
    diff_m!=0; at least not correctly, even if we ignore the sign issue.

    The if statements in Richard Heathfield's version allow it to skip
    unnecessary calculations. It's not clear to me what your version would
    look like if you corrected the problems I've already pointed out. It
    seems, however, that the fundamental difference between his version and
    your version is that your version performs all of the possible
    comparisons, whether or not they're needed, and then tests them multiple
    times. Even if the inefficiency of such a small function isn't important
    in your application, I think Richard's approach is much cleaner.
    James Kuyper, Oct 15, 2008
    #13
  14. 0m

    0m Guest

    On Oct 16, 2:13 am, James Kuyper <> wrote:
    >
    > A comparison function intended for use by qsort() or bsearch() must
    > return a result that is either positive, zero, or negative, depending
    > upon the relative order of the two arguments. Your function always
    > returns a value of 1 if the two arguments are not equal; it never
    > returns a negative value.
    >
    > Your code doesn't seem to handle the cases where diff_y!=0, or
    > diff_m!=0; at least not correctly, even if we ignore the sign issue.
    >


    Thanks for pointing out this. I was not aware that that function was
    intended to be the comparision function of qsort or bsearch. (I'm also
    sorry to misunderstand Richard Heathfield's code.)

    > The if statements in Richard Heathfield's version allow it to skip
    > unnecessary calculations. It's not clear to me what your version would
    > look like if you corrected the problems I've already pointed out. It
    > seems, however, that the fundamental difference between his version and
    > your version is that your version performs all of the possible
    > comparisons, whether or not they're needed, and then tests them multiple
    > times. Even if the inefficiency of such a small function isn't important


    is it better to use this simple line:
    d[0] = t1->tm_year - t2->tm_year;

    instead of Richard Heathfield's:
    diff = (left->tm_year > right->tm_year) - (left->tm_year < right-
    >tm_year);


    My new version attached :) Thanks for read.

    #include <time.h>

    #define CNT 3 /* or 6, plus hour, min, sec fields */

    int cmp_tm(const void *v1, const void *v2)
    {
    const struct tm *t1 = v1;
    const struct tm *t2 = v2;
    int diff, d[CNT], i;

    d[0] = t1->tm_year - t2->tm_year;
    d[1] = t1->tm_mon - t2->tm_mon;
    d[2] = t1->tm_mday - t2->tm_mday;

    /* hour, min, sec similarly */

    for (i = 0; i < CNT; i++)
    if (!(diff = d))
    break;
    return diff;
    }
    0m, Oct 16, 2008
    #14
  15. 0m

    Ian Collins Guest

    Re: is const necessary in eg int compar(const void *, const void*)

    0m wrote:
    >
    > is it better to use this simple line:
    > d[0] = t1->tm_year - t2->tm_year;
    >
    > instead of Richard Heathfield's:
    > diff = (left->tm_year > right->tm_year) - (left->tm_year < right-
    >> tm_year);

    >

    Richard's technique is intended to remove the risk of integer over
    (under?) flow.

    --
    Ian Collins
    Ian Collins, Oct 16, 2008
    #15
  16. 0m

    James Kuyper Guest

    Re: is const necessary in eg int compar(const void *, const void*)

    0m wrote:
    > On Oct 16, 2:13 am, James Kuyper <> wrote:
    >> A comparison function intended for use by qsort() or bsearch() must
    >> return a result that is either positive, zero, or negative, depending
    >> upon the relative order of the two arguments. Your function always
    >> returns a value of 1 if the two arguments are not equal; it never
    >> returns a negative value.
    >>
    >> Your code doesn't seem to handle the cases where diff_y!=0, or
    >> diff_m!=0; at least not correctly, even if we ignore the sign issue.
    >>

    >
    > Thanks for pointing out this. I was not aware that that function was
    > intended to be the comparision function of qsort or bsearch.


    Take a look back to the first message of this discussion. The very first
    sentence specifies that qsort() and bsearch() are the context for the
    question that is the Subject: line in this discussion.

    ....
    > is it better to use this simple line:
    > d[0] = t1->tm_year - t2->tm_year;
    >
    > instead of Richard Heathfield's:
    > diff = (left->tm_year > right->tm_year) - (left->tm_year < right-
    >> tm_year);


    For "reasonable" tm_year values, your approach is perfectly fine.
    However, if you want your function to have no easily avoidable undefined
    behavior, regardless of what inputs are passed to it, then you have to
    consider the possibility that the mathematical value of t1->tm_year -
    t2->tm_year might be greater than INT_MAX, or less than INT_MIN. If that
    is the case, the subtraction has undefined behavior. In practice, on
    many systems a large positive mathematical difference will cause d[0] to
    have a negative value, and vice-versa.

    Richard's version avoids that problem; the result of the subtraction is
    always -1, 0, or 1.


    > My new version attached :) Thanks for read.
    >
    > #include <time.h>
    >
    > #define CNT 3 /* or 6, plus hour, min, sec fields */
    >
    > int cmp_tm(const void *v1, const void *v2)
    > {
    > const struct tm *t1 = v1;
    > const struct tm *t2 = v2;
    > int diff, d[CNT], i;
    >
    > d[0] = t1->tm_year - t2->tm_year;
    > d[1] = t1->tm_mon - t2->tm_mon;
    > d[2] = t1->tm_mday - t2->tm_mday;
    >
    > /* hour, min, sec similarly */
    >
    > for (i = 0; i < CNT; i++)
    > if (!(diff = d))
    > break;
    > return diff;
    > }


    Other than the issue raised above, this looks OK to me, though I might
    be missing something. I'd make diff=d part of the loop condition,
    removing the need for the if() and the break, but many people would
    disapprove of my approach as being hard to read and understand.

    Your approach is marginally less efficient than Richard Heathfield's, in
    that it performs calculations whose results will often not be needed.
    However, in terms of clarity I think it's a toss-up: your loop helps
    avoid RH's repeated if() statements.
    James Kuyper, Oct 16, 2008
    #16
  17. 0m

    jaysome Guest

    On Wed, 15 Oct 2008 08:45:29 +0000, Richard Heathfield
    <> wrote:

    >Nate Eldredge said:
    >
    ><snip>
    >
    >> So therefore it would be good practice in functions accepting `const
    >> void *' arguments to assign to a temporary pointer of the appropriate
    >> type, rather than casting, in order to preserve the usefulness of
    >> const.

    >
    >I'm of the opinion that it's always possible, and usually preferable, to
    >avoid casting.


    If p is of any type other than void *, can (and should) this cast be
    avoided?

    printf("p = %p\n", (void*)p);

    Example:

    #include <stdio.h>
    int main(void)
    {
    double pi = 355 / 113.0; /* close enough for some purposes */
    printf("pi is %f and &pi is %p\n", pi, (void*)&pi);
    return 0;
    }

    --
    jay
    jaysome, Oct 25, 2008
    #17
  18. 0m

    CBFalconer Guest

    jaysome wrote:
    >

    .... snip ...
    >
    > If p is of any type other than void *, can (and should) this cast
    > be avoided?
    >
    > printf("p = %p\n", (void*)p);


    The variadic functions, in particular printf and clones, are
    glaring examples of exceptions to the general rule "casts are
    errors". The reason is that the only type information such
    functions have is the construct of the format string, which is not
    generally known at compile time (the format string may be a
    variable).

    In your example, above, the %p has specified that the functions is
    receiving a void* parameter. So, if p is not of this type, you
    have to convert it with a cast. A good compiler will at least tell
    you if you are making an illegal cast, such as casting an integer
    to a void*.

    --
    [mail]: Chuck F (cbfalconer at maineline dot net)
    [page]: <http://cbfalconer.home.att.net>
    Try the download section.
    CBFalconer, Oct 26, 2008
    #18
  19. CBFalconer <> writes:

    <snip>
    > ... A good compiler will at least tell
    > you if you are making an illegal cast, such as casting an integer
    > to a void*.


    What is "illegal" about it? The conversion is implementation defined,
    but illegal seems way too strong a word.

    --
    Ben.
    Ben Bacarisse, Oct 26, 2008
    #19
  20. 0m

    Guest

    On Oct 26, 2:16 am, Ben Bacarisse <> wrote:
    > CBFalconer <> writes:
    >
    > <snip>
    >
    > > ... A good compiler will at least tell
    > > you if you are making an illegal cast, such as casting an integer
    > > to a void*.

    >
    > What is "illegal" about it? The conversion is implementation defined,
    > but illegal seems way too strong a word.


    The conversion is implementation defined, and one of the possible
    behaviors is undefined behavior.
    If something is UB on an imaginary implementation it's a good enough
    reason for me to avoid that 'something'.
    Chuck could've just been more clear by using a term defined in the
    standard.
    , Oct 27, 2008
    #20
    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. Phlip
    Replies:
    10
    Views:
    578
    Kevin Goodsell
    Sep 29, 2003
  2. Timo Freiberger
    Replies:
    3
    Views:
    944
    Bob Hairgrove
    Oct 30, 2004
  3. Schnoffos
    Replies:
    2
    Views:
    1,212
    Martien Verbruggen
    Jun 27, 2003
  4. Hal Styli
    Replies:
    14
    Views:
    1,634
    Old Wolf
    Jan 20, 2004
  5. Replies:
    11
    Views:
    1,102
Loading...

Share This Page