Timothy Leung
Timothy Leung

Reputation: 1465

char ** then dereference to char *

To compare string using library function, i learnt to write a comparison function to compare two strings, but im not really clear about why are they doing this.

int StrCmp(const void *a, const void *b){
    char *s1 = *(char**) a;
    char *s2 = *(char**) b;
    return strcmp(s1,s2);
}

Why do they have to cast it to char ** first? why not just cast them to char * directly? like

return strcmp((char*)a, (char*)b);

Whats the meaning of casting ( a pointer) to ( a pointer to pointer) If i have

char *x = "string";

if i do casting for x

(char**)x; // what is this? Is this character 's'?

Im quite confuse with this, thanks for clarify and one more question is about the const if it is a const, can i still cast them?(altought i know i can)

Upvotes: 0

Views: 641

Answers (3)

Cloud
Cloud

Reputation: 19333

This is effectively casting a void pointer (ie: void*) to double pointer to char (ie: char**).

Remember that the indirection/de-referencing operator (unary * operator) and the type casting operator (eg: (char**)) have the same precedence, but also right-to-left associativity.

So, consider the following:

void myFunction(void* a) {
  char* s1;  // Pointer to a character, or the beginning of a character array or string
             // Note that the effective difference between a character array and a string is that
             // a string is a character array that is terminated with the '\0' character
  s1 = *(char**) a;
             /* The above statement can be decomposed as follows:
              * (  *( (char**) a ) )
              * 1) Cast the pointer-to-void-type, (ie: void*)'a', 
              *    to a pointer-to-pointer-to-char (ie: char**)
              * 2) Deference the value, so it becomes a pointer-to-char
              * 3) Assign this char* value to s1
              */
}

This is very common in C where you have to do some nifty pointer magic because the language does not support templates like its successor C++. Mind you, if you can work with advanced pointer logic, there's probably no need for you to use C++ in place of C anyways.

Good luck!

Upvotes: 1

Scooter
Scooter

Reputation: 7061

"Why do they have to cast it to (char pointer pointer) first? why not just cast them to (char pointer) directly?"


They have to cast it to what it is. Assuming the value coming in points to memory location 10,000, you have something like this:

  (char **) 

  a                 address a
                    "points to"
                    *a                **a   
  mem 1000          mem 10000         mem 20000
  to mem 1007       to mem 10007
  _____________     _____________     _______________ 
  pointer var  --> char pointer   ->  char
  10000             20000             'a'
  _____________     _____________     _______________

If you cast it to a (char *) you are telling the compiler that the address referred to by a (in this case 10,000) is a char. Which is not correct.

  (char *)          address a points 
  a                 to
  mem 1000          mem 10000         mem 20000
  to mem 1007
  _____________     ______            ________________
  pointer var  -->  "char"            unreachable char
  10000             ?                 'a'
  _____________     ______            ________________

"Whats the meaning of casting ( a pointer) to ( a pointer to pointer) If i have

char *x = "string"; if i do casting for x

(char**)x; // what is this? Is this character 's'?"


What exists in this example is something like:

x            *x or x[0] x[1]       x[2]      x[3]      x[4]      x[5]      x[6]
mem 15000    mem 30000  mem 30001  mem 30002 mem 30003 mem 30004 mem 30005 mem 30006
to 15007
char *       char       char       char      char      char      char      char
_________    _________  _________  _________ _________ _________ _________ _________

30000    -->    's'       't'         'r'      'i'      'n'       'g'       '\0'
________     _________  _________  _________ _________ _________ _________ _________

If you tell the compiler that x is a char** then it thinks this is the pattern:

x              *x or x[0]         **x
               char *
mem 15000      mem 30000          "char" 
to 15007       to 30007
__________     _________          _________

30000      --> "string\0?"
               converted to
               pointer value  ->   ??
__________     _________          _________

It incorrectly ends up going to whatever "address" the first 8 bytes starting at 30000 resolves to and getting a "character". But since 30000 is the start of a null-terminated character array, at best it goes to some part of memory and gets some random byte, thinking it is a valid char. At worst it will get an address that is invalid for this program, causing a fatal error when it tries to access it.


so can i just do

 return strcmp((char *)a, (char *)b);

No, because a and b are not char pointers. To get char pointers you can't avoid:

return strcmp( *(char**)a, *(char**)b);

Using Linden's example you would call like so:

#include <stdio.h>
#include <string.h>

int StrCmp(const void *a, const void *b){
    char *s1 = *(char**) a;
    char *s2 = *(char**) b;
    printf("s1:%s\n",s1);
    printf("s2:%s\n",s2);
    return strcmp(s1,s2);
}

const char* arr_of_ptr[] =
{
  "hello",
  "world"
};

const char **p_arr_of_ptr = arr_of_ptr;

int main(void)
{
   const char *cstring1 = "LaDonna";
   const char *cstring2 = "McPherson";
   const char **pcstring1 = &cstring1;
   const char **pcstring2 = &cstring2;
   StrCmp(&arr_of_ptr[0],&arr_of_ptr[1]);
   StrCmp(pcstring1,pcstring2);
   StrCmp(p_arr_of_ptr,p_arr_of_ptr + 1);
}

Upvotes: 4

Lundin
Lundin

Reputation: 213842

Most likely the caller of that function is using an array of pointers, such as:

const char* arr_of_ptr[] =
{
  "hello",
  "world"
};

In that case, the first element of the array is a pointer to a char, it is not a char in itself. Therefore the StrCmp function acts as a translator between the array of pointers to normal C strings.

However, they are also casting away the const keyword, which is bad practice. The function should be written like this:

int StrCmp(const void *a, const void *b){
    const char *s1 = *(const char**) a;
    const char *s2 = *(const char**) b;
    return strcmp(s1,s2);
}

Good reading on the topic.

Upvotes: 4

Related Questions