Thread: casting - pointer to pointer

  1. #1
    Registered User
    Join Date
    Apr 2008
    Posts
    31

    Cool casting - pointer to pointer

    Can anyone look at the code below and answer the question given in the code...

    Code:
    int compare (const void *p1, const void *p2)
    {
      const char *i;
      const char *j;
    
      i = *((char **)p1); /* WHAT THE HECK IS THIS*/
      j = *((char **)p2);/*Why is the '*' operator 3 times in a line */
    
      if (strlen(i) > strlen(j))
        return 1;
      else if (strlen(i) < strlen(j))
        return -1;
      else
        return strcmp(i,j);
    }

  2. #2
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Its casting a void pointer to a char pointer pointer (pointer to pointer to char), then dereferencing that to get a char pointer that represents a string.

    A char pointer pointer can also be looked at as a pointer to a string. So it's casting void to string pointer, and dereferencing that to get the actual string to compare.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  3. #3
    Registered User
    Join Date
    Apr 2008
    Posts
    31

    Post

    Quote Originally Posted by King Mir View Post
    A char pointer pointer can also be looked at as a pointer to a string. .
    Can you give an example on this?

  4. #4
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by terminator View Post
    Can you give an example on this?
    Sure:
    Code:
    int compare (const void *p1, const void *p2)
    {
      const char *i;
      const char *j;
    
      i = *((char **)p1); 
      j = *((char **)p2);
    
      if (strlen(i) > strlen(j))
        return 1;
      else if (strlen(i) < strlen(j))
        return -1;
      else
        return strcmp(i,j);
    }
    ((char **)p1) is a pointer to a string; the * in front dereferences it to get the actual string itself. Notice i is of type "const char *" -- the char * means string, more or less (nitpickingly honest: a pointer to a character, but strlen and strcmp are going to interpret it as a pointer to the first character of a series) and the const means that those characters can not be altered.

  5. #5
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    I can't see why that code is unnecessarily complicated?
    Instead of:
    Code:
    *((char **)p1)
    wouldn't this be much simpler?
    Code:
    (char *)p1
    or to be const-correct:
    Code:
    (const char *)p1

  6. #6
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    In C, there is probably little reason for any cast at all.
    7. It is easier to write an incorrect program than understand a correct one.
    40. There are two ways to write error-free programs; only the third one works.*

  7. #7
    Banned master5001's Avatar
    Join Date
    Aug 2001
    Location
    Visalia, CA, USA
    Posts
    3,685
    Quote Originally Posted by cpjust View Post
    I can't see why that code is unnecessarily complicated?
    Instead of:
    Code:
    *((char **)p1)
    wouldn't this be much simpler?
    Code:
    (char *)p1
    or to be const-correct:
    Code:
    (const char *)p1
    Of course its easier. But only in the same sense as its easier to write

    Example
    Code:
    int j;
    
    for(;j < 5; j++)
    {
    
    }
    Example
    Code:
    int j;
    
    scanf("&#37;d", j);
    Man those sure saved me a whole lot of typing. Too bad my code is invalid. Lets use some typedefs (I know how much Elysa hates them but oh well).

    Code:
    typedef char **type1;
    typedef char *type2;
    Ok, this isn't the best type name convention, however it paints the picture I am wanting you to see. A type1 is certainly not a type2, right? In fact, a type1 is a pointer to a type2.

    Code:
    int compare(const void *a, const void *b)
    {
       // void* can be ANYTHING. But today we want the void pointer to be a type2.
       type1 xptr = (type1)a;
       type1 yptr = (type1)b;
    
       // Remembering that xptr and yptr are both pointers to type2 objects we need to remember
       // to compare what they point to.
       type2 x = *xptr;
       type2 y = *yptr;
    
       // do whatever here.
    }
    I hope that helps. And to clear up one issue for Dave, its ok to do without casting when it comes to a void*, but not a void**. So int *j = malloc(5) is cool. But int **j = malloc(5) requires a cast. I hope that helps too.

  8. #8
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    Quote Originally Posted by master5001 View Post
    So int *j = malloc(5) is cool. But int **j = malloc(5) requires a cast. I hope that helps too.
    Both kinda demonstrate my point. [edit]No, a different but related one.

    [edit]
    Code:
    int **foo = malloc(5 * sizeof *foo);
    Last edited by Dave_Sinkula; 04-20-2008 at 08:54 PM. Reason: Second edit was much later.
    7. It is easier to write an incorrect program than understand a correct one.
    40. There are two ways to write error-free programs; only the third one works.*

  9. #9
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Quote Originally Posted by master5001 View Post
    Of course its easier. But only in the same sense as its easier to write

    Example
    Code:
    int j;
    
    for(;j < 5; j++)
    {
    
    }
    Example
    Code:
    int j;
    
    scanf("%d", j);
    Man those sure saved me a whole lot of typing. Too bad my code is invalid. Lets use some typedefs (I know how much Elysa hates them but oh well).

    Code:
    typedef char **type1;
    typedef char *type2;
    Ok, this isn't the best type name convention, however it paints the picture I am wanting you to see. A type1 is certainly not a type2, right? In fact, a type1 is a pointer to a type2.

    Code:
    int compare(const void *a, const void *b)
    {
       // void* can be ANYTHING. But today we want the void pointer to be a type2.
       type1 xptr = (type1)a;
       type1 yptr = (type1)b;
    
       // Remembering that xptr and yptr are both pointers to type2 objects we need to remember
       // to compare what they point to.
       type2 x = *xptr;
       type2 y = *yptr;
    
       // do whatever here.
    }
    I hope that helps. And to clear up one issue for Dave, its ok to do without casting when it comes to a void*, but not a void**. So int *j = malloc(5) is cool. But int **j = malloc(5) requires a cast. I hope that helps too.
    But my point is -- why cast to a double pointer and then dereference one of them if you can just cast directly to a single pointer?

  10. #10
    Registered User
    Join Date
    Oct 2001
    Posts
    2,934
    >So int *j = malloc(5) is cool. But int **j = malloc(5) requires a cast. I hope that helps too.
    No actually in C, an malloc() never requires a cast.

  11. #11
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Quote Originally Posted by cpjust View Post
    I can't see why that code is unnecessarily complicated?
    Instead of:
    Code:
    *((char **)p1)
    wouldn't this be much simpler?
    Code:
    (char *)p1
    or to be const-correct:
    Code:
    (const char *)p1
    Nope, the second one is completely wrong. You can't subtract the *'s. This function is obviously being used as an argument to qsort which is to sort an array of char*'s a.k.a strings.

    Original code: The *'s in the cast itself don't do anything besides tell the compiler what type of thing the void pointer is supposed to be. This doesn't generate any code whatsoever. The outer star on the left actually tells the compiler to dereference the thing on the right. That means asking the compiler to generate code that fetches what is at some memory address, and viola you have correct code. There is no simpler way to do this. It must cast to a char**!

    Your incorrect code: The single char* cast tells the compiler (incorrectly) that the data is already a char pointer. This in itself generates no code at all. It does not look up and memory address to get the correct pointer value, and will produce non-working code.

    Even Dave_Sinkula seems to have misunderstood this one, going by his first comment - Must be having an off day.
    King Mir has a good explanation.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  12. #12
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by master5001 View Post
    Lets use some typedefs (I know how much Elysa hates them but oh well).
    I only hate pointer typedefs, to those who do not know. They destroy readability.
    Anyway, I think the easiest way to see what this code does is:

    Code:
    char** p1 = malloc(char*);
    *p = malloc(2 * sizeof(**p));
    (*p)[0] = 'a';
    (*p)[1] = 0;
    
    compare(p1, p1);
    free(*p1);
    free(p1);
    /* Now what's going to happen if you cast it to char* directly!?! */
    
    const char *i;
    i = (char*)p1; /* Oops, i now points to a char**, not char* */
    i = *(char**)p1; /* Correct, i now points to a char* */
    Since the code that calls the function passes a char**, the function must as well, or it will eventually parse the char** and not char*.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  13. #13
    Registered User
    Join Date
    Apr 2008
    Posts
    31
    Is there any good book or website covering this complex pointer declarations & casting?

  14. #14
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  15. #15
    Registered User
    Join Date
    Apr 2008
    Posts
    31
    Quote Originally Posted by Elysia View Post
    there is only little explanation about pointer to pointer...
    Is there any good book or website with examples?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. casting contents of pointer?
    By nutsNguts in forum C Programming
    Replies: 8
    Last Post: 11-10-2008, 11:07 AM
  2. Ban pointers or references on classes?
    By Elysia in forum C++ Programming
    Replies: 89
    Last Post: 10-30-2007, 03:20 AM
  3. scope of a pointer?
    By Syneris in forum C++ Programming
    Replies: 6
    Last Post: 12-29-2005, 09:40 PM
  4. Question About Pointer To Pointer
    By BlitzPackage in forum C++ Programming
    Replies: 2
    Last Post: 09-19-2005, 10:19 PM
  5. pointers
    By InvariantLoop in forum C Programming
    Replies: 13
    Last Post: 02-04-2005, 09:32 AM