Function pointer question P119 K&R

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • mdh

    Function pointer question P119 K&R

    May I ask the following.

    By K&R's own admission, the example used to describe function pointers
    is complex ( on P119). In addition, the use of casts has been stated
    by some on this group as being, again, a poor/bad example of it's use.
    For the moment, accepting these criticisms, I would still like to get
    some insight into why/how some things work as even poor code is
    enlightening, to me at least.

    The example uses K&R's version of qsort ( not the standard one) and
    the declaration is as follows:

    void qsort(void *lineptr[], int left, int right. int (*comp)(void *,
    void *));

    The example then chooses from one of 2 methods for comparing strings,
    whose declaration are:

    int numcmp (*char, *char);
    int strcmp( *char, *char);

    The call to qsort is as follows:

    qsort (void**)lineptr , 0, nlines-1, int (*)(void*, void*)) numeric?
    numcmp:strcm)); /* numeric is an integer set based on an optional
    command line argument) */

    One last declaration before my question.

    The swap function is declared as:

    void swap(void *[], int, int);

    Within the qsort function, the call to the function pointer is as
    follows:

    if ((*comp)(v[i], v[left] ) < 0)
    swap (v, ++last, i);

    If qsort casts the comparison function to void pointers, as it does in
    "main", then why is it ok for the call in qsort to pass as arguments
    to void pointers when the functions numcmp/strcmp expect as arguments
    2 char pointers? Or..I might be totally confusing the issue, which is
    equally likely. Swap seems to act as I would expect, ie it receives as
    it's arguments, an array of pointers to void, plus 2 integers.

    thanks in advance.


  • Ben Bacarisse

    #2
    Re: Function pointer question P119 K&amp;R

    mdh <mdeh@comcast.n etwrites:

    <snip>
    The example uses K&R's version of qsort ( not the standard one) and
    the declaration is as follows:
    >
    void qsort(void *lineptr[], int left, int right. int (*comp)(void *,
    void *));
    >
    The example then chooses from one of 2 methods for comparing strings,
    whose declaration are:
    >
    int numcmp (*char, *char);
    int strcmp( *char, *char);
    >
    The call to qsort is as follows:
    >
    qsort (void**)lineptr , 0, nlines-1, int (*)(void*, void*)) numeric?
    numcmp:strcm)); /* numeric is an integer set based on an optional
    command line argument) */
    >
    One last declaration before my question.
    >
    The swap function is declared as:
    >
    void swap(void *[], int, int);
    >
    Within the qsort function, the call to the function pointer is as
    follows:
    >
    if ((*comp)(v[i], v[left] ) < 0)
    swap (v, ++last, i);
    We now have v. The prototype you showed had lineptr. I'll assume the
    v is a void ** like lineptr.
    If qsort casts the comparison function to void pointers, as it does in
    "main", then why is it ok for the call in qsort to pass as arguments
    to void pointers when the functions numcmp/strcmp expect as arguments
    2 char pointers?
    It is not OK (in general). It works in practise because void * and
    char * must have the same size and representation but if the lineptr
    array were, say, an array of struct pointers it might very well fail
    on a real machine (albeit an old one).

    You can convert a function pointer to any type of function pointer you
    like but to be safe, when you call it, you have converted it back to
    (a pointer to) the type of the function you are actually calling and
    you must pass arguments that are "acceptable " as per the normal
    function call rules. I say "acceptable " because the exact rules are
    rather wordy and are not really the subject of our question.
    Or..I might be totally confusing the issue, which is
    equally likely. Swap seems to act as I would expect, ie it receives as
    it's arguments, an array of pointers to void, plus 2 integers.
    Yes, the call to swap is fine.

    --
    Ben.

    Comment

    • m

      #3
      Re: Function pointer question P119 K&amp;R

      In article <87tzezan2c.fsf @bsb.me.uk>,
      Ben Bacarisse <ben.usenet@bsb .me.ukwrote:

      >
      We now have v. The prototype you showed had lineptr. I'll assume the
      v is a void ** like lineptr.
      >
      If qsort casts the comparison function to void pointers, as it does in
      "main", then why is it ok for the call in qsort to pass as arguments
      to void pointers when the functions numcmp/strcmp expect as arguments
      2 char pointers?
      >
      It is not OK (in general). It works in practise because void * and
      char * must have the same size and representation but if the lineptr
      array were, say, an array of struct pointers it might very well fail
      on a real machine (albeit an old one).
      >
      Ben, you say that void * and char * must have the same size and
      representation. Are you saying that this is a fact in C, or are you
      saying that it must be so for the program to work as intended and it is
      up to the programmer to ensure that this fact is true.

      Comment

      • Richard Heathfield

        #4
        Re: Function pointer question P119 K&amp;R

        m said:

        <snip>
        Ben, you say that void * and char * must have the same size and
        representation. Are you saying that this is a fact in C,
        It's a fact in C.

        Buried deep in 3.1.2.5 we find that "A pointer to void shall have the same
        representation and alignment requirements as a pointer to a character
        type. Other pointer types need not have the same representation or
        alignment requirements."

        --
        Richard Heathfield <http://www.cpax.org.uk >
        Email: -http://www. +rjh@
        Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
        "Usenet is a strange place" - dmr 29 July 1999

        Comment

        • santosh

          #5
          Re: Function pointer question P119 K&amp;R

          m wrote:
          In article <87tzezan2c.fsf @bsb.me.uk>,
          Ben Bacarisse <ben.usenet@bsb .me.ukwrote:
          >
          >
          >>
          >We now have v. The prototype you showed had lineptr. I'll assume
          >the v is a void ** like lineptr.
          >>
          If qsort casts the comparison function to void pointers, as it does
          in "main", then why is it ok for the call in qsort to pass as
          arguments to void pointers when the functions numcmp/strcmp expect
          as arguments 2 char pointers?
          >>
          >It is not OK (in general). It works in practise because void * and
          >char * must have the same size and representation but if the lineptr
          >array were, say, an array of struct pointers it might very well fail
          >on a real machine (albeit an old one).
          >>
          >
          Ben, you say that void * and char * must have the same size and
          representation. Are you saying that this is a fact in C, or are you
          saying that it must be so for the program to work as intended and it
          is up to the programmer to ensure that this fact is true.
          No, it is required by the Standard. This was because before
          Standardisation , char* was often used as a "generic" pointer type. The
          Standard codified a separate type for this (void*), but to maintain
          compatibility with pre-Standard code and ease the transition to
          Standard C, it retained this property of char* too.

          IIRC K&R2 explains this in appendix A.

          Comment

          • m

            #6
            Re: Function pointer question P119 K&amp;R

            In article <baednVK3l8yN 1-nVnZ2dnUVZ8redn Z2d@bt.com>,
            Richard Heathfield <rjh@see.sig.in validwrote:
            m said:
            >
            <snip>
            >
            Ben, you say that void * and char * must have the same size and
            representation. Are you saying that this is a fact in C,
            >
            It's a fact in C.
            >
            Thank you Richard. At some point...I would really like to hear your
            take on the issue of the misuse of casts.

            BTW...I am not sure if you have noticed how little traffic there
            is...Google is totally messed up...about 18 hours behind.

            Comment

            • m

              #7
              Re: Function pointer question P119 K&amp;R

              In article <g51t3p$n9m$2@r egistered.motza rella.org>,
              santosh <santosh.k83@gm ail.comwrote:
              >
              It is not OK (in general). It works in practise because void * and
              char * must have the same size and representation but if the lineptr
              array were, say, an array of struct pointers it might very well fail
              on a real machine (albeit an old one).
              >
              No, it is required by the Standard. This was because before
              Standardisation , char* was often used as a "generic" pointer type. The
              Standard codified a separate type for this (void*), but to maintain
              compatibility with pre-Standard code and ease the transition to
              Standard C, it retained this property of char* too.
              >
              IIRC K&R2 explains this in appendix A.

              Thank you Santosh...not quite at the appendix yet!

              Comment

              • rahul

                #8
                Re: Function pointer question P119 K&amp;R

                On Jul 9, 5:10 am, mdh <m...@comcast.n etwrote:
                int numcmp (*char, *char);
                int strcmp( *char, *char);
                int numcmp(char *, char *);
                int strcmp(char *, char *);

                >
                qsort (void**)lineptr , 0, nlines-1, int (*)(void*, void*)) numeric?
                numcmp:strcm));
                I assume you meant it to be:
                qsort ((void**)linept r, 0, nlines-1, (int (*)(void*, void*)) (numeric?
                numcmp:strcm));


                >
                If qsort casts the comparison function to void pointers, as it does in
                "main",
                qsort is casting the function pointer to a pointer to a function
                taking 2 pointer to a void parameters and returning int.
                to void pointers when the functions numcmp/strcmp expect as arguments
                2 char pointers?
                char and void pointers are required to have compatible representation.
                Still, the correct way to do it is to declare numcmp as taking void *
                and cast it in the function itself.

                int numcmp(void *foo, void *bar) {
                return *(int *)foo - *(int *)bar;
                }





                Comment

                • Richard Heathfield

                  #9
                  Re: Function pointer question P119 K&amp;R

                  rahul said:

                  <snip>
                  Still, the correct way to do it is to declare numcmp as taking void *
                  and cast it in the function itself.
                  >
                  int numcmp(void *foo, void *bar) {
                  return *(int *)foo - *(int *)bar;
                  No, qsort takes int(*)(const void *, const void *), not int (*)(void *,
                  void *).

                  And there's no need for a cast if you don't want one. And there's no need
                  to risk overflow, either.

                  int numcmp(const void *vleft, const void *vright)
                  {
                  const int *left = vleft;
                  const int *right = vright;
                  return (*left *right) - (*left < *right);
                  }

                  Note, however, that K&R2's comparison routine takes string inputs, so we
                  would instead need:

                  #include <stdlib.h>
                  int numcmp(const void *vleft, const void *vright)
                  {
                  const char *sleft = vleft;
                  const char *sright = vright;
                  double left = strtod(sleft, NULL);
                  double right = strtod(sright, NULL);
                  return (left right) - (left < right);
                  }

                  K is a marvellous chap and all that, but he's just a bit too fond of casts
                  for my taste.

                  --
                  Richard Heathfield <http://www.cpax.org.uk >
                  Email: -http://www. +rjh@
                  Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
                  "Usenet is a strange place" - dmr 29 July 1999

                  Comment

                  • Ben Bacarisse

                    #10
                    Re: Function pointer question P119 K&amp;R

                    m <mdeh@comcast.n etwrites:
                    In article <baednVK3l8yN 1-nVnZ2dnUVZ8redn Z2d@bt.com>,
                    Richard Heathfield <rjh@see.sig.in validwrote:
                    >
                    >m said:
                    >>
                    ><snip>
                    >>
                    Ben, you say that void * and char * must have the same size and
                    representation. Are you saying that this is a fact in C,
                    >>
                    >It's a fact in C.
                    >>
                    >
                    Thank you Richard. At some point...I would really like to hear your
                    take on the issue of the misuse of casts.
                    But it is probably worth pointing out that the code is still a
                    technical violation. The wording about representations and alignment
                    mean that it is hard to see what an implementation could be doing that
                    would make the code go wrong but it is still, technically, UB:

                    6.5.2.2 p9: "If the function is defined with a type that is not
                    compatible with the type (of the expression) pointed to by the
                    expression that denotes the called function, the behavior is
                    undefined."

                    You need to read an awful lot to be sure, but the function types
                    pointed to by 'int (*)(void *, void *)' and 'int (*)(char *, char *)'
                    are not compatible -- even allowing for the special wording about char
                    * and void *.

                    --
                    Ben.

                    Comment

                    • mdh

                      #11
                      Re: Function pointer question P119 K&amp;R

                      On Jul 8, 10:22 pm, Richard Heathfield <r...@see.sig.i nvalidwrote:
                      m said:
                      >
                      <snip>
                      >
                      Ben, you say that void * and char * must have the same size and
                      representation. Are you saying that this is a fact in C,
                      >
                      >
                      >
                      Buried deep in 3.1.2.5 we find that "A pointer to void shall have the same
                      representation and alignment requirements as a pointer to a character
                      type.  Other pointer types need not have the same representation or
                      alignment requirements."
                      >


                      Not to flog a dead horse, does this then mean that when a void pointer
                      is passed to function (foo) expecting a character pointer, foo simply
                      cannot tell the difference between what it is expecting and what it is
                      getting, or that the compiler does not complain as **it** knows that
                      there is no difference between the two and thus allows this step to
                      occur without any warning?

                      Comment

                      • Richard Heathfield

                        #12
                        Re: Function pointer question P119 K&amp;R

                        mdh said:
                        On Jul 8, 10:22 pm, Richard Heathfield <r...@see.sig.i nvalidwrote:
                        <snip>
                        >Buried deep in 3.1.2.5 we find that "A pointer to void shall have the
                        >same representation and alignment requirements as a pointer to a
                        >character type. Other pointer types need not have the same
                        >representati on or alignment requirements."
                        >
                        Not to flog a dead horse, does this then mean that when a void pointer
                        is passed to function (foo) expecting a character pointer, foo simply
                        cannot tell the difference between what it is expecting and what it is
                        getting, or that the compiler does not complain as **it** knows that
                        there is no difference between the two and thus allows this step to
                        occur without any warning?
                        It doesn't mean either of those things. When a void pointer is passed to a
                        function that expects a character pointer, C provides an automatic
                        conversion from void * to char *, just as it would between void * and any
                        other pointer-to-object or pointer-to-incomplete-type. In this situation
                        the representationa l similarities of void * and char * are irrelevant
                        (although they obviously make the conversion very simple for the
                        implementation to achieve).

                        --
                        Richard Heathfield <http://www.cpax.org.uk >
                        Email: -http://www. +rjh@
                        Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
                        "Usenet is a strange place" - dmr 29 July 1999

                        Comment

                        • mdh

                          #13
                          Re: Function pointer question P119 K&amp;R

                          On Jul 9, 8:50 pm, Richard Heathfield <r...@see.sig.i nvalidwrote:
                          >
                          Not to flog a dead horse,.......
                          >
                          It doesn't mean either of those things. When a void pointer is passed to a
                          function that expects a character pointer, C provides an automatic
                          conversion from void * to char *, just as it would between void * and any
                          other pointer-to-object or pointer-to-incomplete-type. In this situation
                          the representationa l similarities of void * and char * are irrelevant
                          (although they obviously make the conversion very simple for the
                          implementation to achieve).
                          >

                          thanks Richard

                          Comment

                          • Barry Schwarz

                            #14
                            Re: Function pointer question P119 K&amp;R

                            On Wed, 9 Jul 2008 20:23:18 -0700 (PDT), mdh <mdeh@comcast.n etwrote:
                            >On Jul 8, 10:22 pm, Richard Heathfield <r...@see.sig.i nvalidwrote:
                            >m said:
                            >>
                            ><snip>
                            >>
                            Ben, you say that void * and char * must have the same size and
                            representation. Are you saying that this is a fact in C,
                            >>
                            >>
                            >>
                            >Buried deep in 3.1.2.5 we find that "A pointer to void shall have the same
                            >representati on and alignment requirements as a pointer to a character
                            >type.  Other pointer types need not have the same representation or
                            >alignment requirements."
                            >>
                            >
                            >
                            >
                            >Not to flog a dead horse, does this then mean that when a void pointer
                            >is passed to function (foo) expecting a character pointer, foo simply
                            >cannot tell the difference between what it is expecting and what it is
                            >getting, or that the compiler does not complain as **it** knows that
                            >there is no difference between the two and thus allows this step to
                            >occur without any warning?
                            There is a difference between the function knowing what to expect and
                            the compiler knowing what the function expects. If the prototype
                            tells the compiler the function is expecting a non-void object
                            pointer, then any void* whose value is properly aligned for the object
                            type will be converted automatically. Similarly, if the compiler
                            knows that the function is expecting a void*, then any object pointer
                            will be converted automatically. This is a result only of the
                            implicit conversion between void* and object pointer and has nothing
                            to do with the mandatory similarity between void* and char*.

                            On the other hand, consider the case of a variadic function like
                            printf. When processing a %p format specification, the function
                            expects a void*. However, the compiler has no idea and will pass a
                            char* without modification. (Before someone notes that some compilers
                            do in fact check the format string, I remind them that the format
                            string need not be a literal and the compiler may not know it's
                            contents.) In the unlikely event that there is a different passing
                            mechanism for void* and char*, you have undefined behavior in spite of
                            the mandatory similarity between the two.


                            Remove del for email

                            Comment

                            • mdh

                              #15
                              Re: Function pointer question P119 K&amp;R

                              On Jul 9, 9:26 pm, Barry Schwarz <schwa...@dqel. comwrote:
                              On Wed, 9 Jul 2008 20:23:18 -0700 (PDT), mdh <m...@comcast.n etwrote:
                              On Jul 8, 10:22 pm, Richard Heathfield <r...@see.sig.i nvalidwrote:
                              m said:
                              >
                              <snip>
                              >
                              Ben, you say that void * and char * must have the same size and
                              representation. Are you saying that this is a fact in C,
                              >
                              Buried deep in 3.1.2.5 we find that "A pointer to void shall have the same
                              representation and alignment requirements as a pointer to a character
                              type.  Other pointer types need not have the same representation or
                              alignment requirements."
                              >
                              Not to flog a dead horse, does this then mean that when a void pointer
                              is passed to  function (foo) expecting a character pointer, foo simply
                              cannot tell the difference between what it is expecting and what it is
                              getting, or that the compiler does not complain as **it** knows that
                              there is no difference between the  two and thus allows this step to
                              occur without any warning?
                              >
                              There is a difference between the function knowing what to expect and
                              the compiler knowing what the function expects.  If the prototype
                              tells the compiler the function is expecting a non-void object
                              pointer, then any void* whose value is properly aligned for the object
                              type will be converted automatically.  Similarly, if the compiler
                              knows that the function is expecting a void*, then any object pointer
                              will be converted automatically.  This is a result only of the
                              implicit conversion between void* and object pointer and has nothing
                              to do with the mandatory similarity between void* and char*.
                              >
                              On the other hand, consider the case of a variadic function like
                              printf.  When processing a %p format specification, the function
                              expects a void*.  However, the compiler has no idea and will pass a
                              char* without modification.  (Before someone notes that some compilers
                              do in fact check the format string, I remind them that the format
                              string need not be a literal and the compiler may not know it's
                              contents.)  In the unlikely event that there is a different passing
                              mechanism for void* and char*, you have undefined behavior in spite of
                              the mandatory similarity between the two.
                              >
                              Remove del for email
                              Barry, thank you for that erudite explanation. It really adds to my
                              understanding of C.

                              Comment

                              Working...