0

The compareStrings is going to compare two strings. The code below works fine when the void pointer is cast to char pointer.

int compareStrings(void *value1, void *value2) {
    const char *str1, *str2;

    str1 =  (char *)value1;
    str2 =  (char *)value2;

    return strcmp(str1, str2);
}

However, when I cast the type to pointer to pointer to a char, the code dumps segmentation fault error which I think, reasonably, it should not.

int compareStrings(void *value1, void *value2) {
    const char **str1, **str2;

    str1 =  (char **)value1;
    str2 =  (char **)value2;

    return strcmp(*str1, *str2);
}

Can anyone explain the problem with the second function?

UPDATE:

The complete code is:

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


int compare(void *value1, void *value2);
int binarySearch(void **array, int size, void *value,
             int(*compareFunc)(void *, void *)); 
int compareStrings(void *value1, void *value2);

int binarySearch(void **array, int size, void *value,
             int(*compareFunc)(void *, void *)) {
    int low, mid, high, compareResult;

    low = 0;
    high = size;
    while (low < high) {
        mid = low + ((high - low) / 2);
        compareResult = (*compareFunc)(array[mid], value);
        if (compareResult < 0) {
            low = mid + 1;
        } else {
            high = mid;
        }
    }

    return low;
}

int compareStrings(void *value1, void *value2) {
    const char *str1, *str2;

    str1 =  (char *)value1;
    str2 =  (char *)value2;     

    return strcmp(str1, str2);
}


int main() {
    int nElements, maxStringLen, index;
    char **stringArray;
    char *sToFind;

    nElements = 10;
    maxStringLen = 100;   
    sToFind = NULL;
    stringArray = malloc(sizeof(char *) * nElements);

    for (int i = 0; i < nElements; i++) {
        stringArray[i] = malloc(sizeof(char) * maxStringLen);
        sprintf(stringArray[i], "sample%d", i+1);
    }

    sToFind = "sample3";
    index = binarySearch((void **)stringArray, nElements, sToFind,         compareStrings);
    if (index >= nElements) {
        printf ("ERROR: value %s not found at index %d\n", sToFind, index);
    }else{
        printf("item found at index %d!\n", index);     
    }

    for(int i = 0; i < nElements; i++) {
        free(stringArray[i]);
    }
    free(stringArray);

    return 0;   
} 
greenwithe
  • 21
  • 1
  • 3
  • 2
    If you haven't changed the caller code between the two implementations, it will obviously not work. – Eugene Sh. Feb 27 '18 at 18:29
  • 3
    What are you trying to do? The prototype looks like something that's called by `bsearch` or `qsort` (but they should have `const void*` parameters instead, so...), but it's impossible to debug without the original call site. – Daniel Kamil Kozar Feb 27 '18 at 18:30
  • 5
    This depends entirely on how this function is called. Please update your question with a [mcve]. I suspect you're using it as a callback function to the `qsort` function, so in particular show how your list is set up and how you call `qsort`. – dbush Feb 27 '18 at 18:30
  • 2
    This looks like it is probably a duplicate of [How to `qsort()` an array of pointers to `char` in C?](https://stackoverflow.com/questions/3489139/how-to-qsort-an-array-of-pointers-to-char-in-c/3489596#3489596) — or, at least, that has information to offer that will help you. Your first comparator is suitable for sorting the characters in a string; the second is suitable for sorting an array of pointers to strings. The calls to `qsort()` are different, just as the comparators are different. – Jonathan Leffler Feb 27 '18 at 18:46
  • 1
    If you know you have char pointers, why are you passing them around as void pointers? This just makes things harder to understand and more error-prone, and it means the compiler can't help you catch your mistakes. (Unless, as others have speculated, you're using `qsort` on arrays of pointers.) – Steve Summit Feb 27 '18 at 19:30

2 Answers2

0

The problem is that your are casting a pointer and not transforming it into a pointer to a pointer.

The cast (char **) just change the type of the following expression, it do not transform it. So the original and the resulting pointer are still the same pointer (they point the same place in memory), just the type is different. So both points to a string. But with your cast you lie to the compiler: you are telling him that you know what you are doing, and that the pointer point to a pointer to a string.

So you get a segmentation fault: the pointer point to a text and not to a pointer to a text: you are deferencing a string and oops.

You should have written:

str1 =  (char **)&value1;

Note: your const char * is probably wrong. You are saying that the string is constant (and only the first one), not that the variable is constant (which should be written char * const).

Giacomo Catenazzi
  • 8,519
  • 2
  • 24
  • 32
0

when I cast the type to pointer to pointer to a char, the code dumps segmentation fault error which I think, reasonably, it should not.

When you cast the pointer to pointer that operation does not affect the casted pointer. It still points to same memory. This is demonstrated by printout 3.

1. Original string addresses:
Address of str1= 0x55bc799fea65
Address of str2= 0x55bc799fea6a

3. The addresses after casting `(char **)` do not change:
Address of  (char **)value1= 0x55bc799fea65
Address of  (char **)value2= 0x55bc799fea6a

Casting is not & or * operation!

When you do dereferencing by * that operation dereferences the value of the original pointer.

The result is not what you want:

4.
Address of *(char **)value1= 0x343332 00 34 33 32 31
Address of *(char **)value2= 0x31     00 35 34 33 32    

These are not the addresses as you expected but the values of the strings.

Those values are not the valid addresses! When you try to use them as the string addresses you will get segmentation fault in the strcmp function.

Take a look at the demo program:

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

void print_addresses(char *nr, char *d1, void *a1, char *d2, void *a2)
{
    printf("%s.\nAddress of %s= %p\nAddress of %s= %p\n\n",nr, d1, a1, d2, a2);
}

int compareStrings1 (void *value1, void *value2)
{
    char *str1, *str2;

    str1 = (char *) value1;  
    str2 = (char *) value2;

    //2.
    print_addresses("2", "str1", str1, "str2", str2);

    return strcmp(str1, str2);
}

int compareStrings2(void *value1, void *value2) {
    char **str1, **str2;
    char **s1, **s2;    

    str1 =  (char **)value1;  //  warning: assignment from incompatible pointer type 
    str2 =  (char **)value2;  //  warning: assignment from incompatible pointer type 

   //3. Addresses after casting `(char **)` do not change:
    print_addresses( "3",  " (char **)value1", str1, " (char **)value2", str2);  // str1,str2 are still pointing to the original strings!
    //---------------------------------------------------------------------------------------------

    print_addresses( "4", "*(char **)value1", *str1, "*(char **)value2", *str2);  // '*' this dereferences the address to the value of the first character

    printf("(*str1)= %c\n", *str1);       //'*' this dereferences the address to the value of the first character
    printf("(*str2)= %c\n\n", *str2);       //'*' this dereferences the address to the value of the first character 

    // Now:

    s1 =  (char **) &value1;             // '&'' gives us pointer to pointer
    s2 =  (char **) &value2;             // '&'' gives us pointer to pointer

    //5.
    print_addresses( "5", " (char **) & value1" ,   s1, " (char **) & value2",   s2);    // pointer to pointer address 

    //6.
    print_addresses( "6", "*(char **) & value1",   *s1, "*(char **) & value2",  *s2);    // dereferencing pointer to pointer

    return strcmp(*s1, *s2);             // OK!
}

int main()
{
    char *str1 = "1234";
    char *str2 = "2345";  // 5 bytes

    //1.  Original string addresses:
    print_addresses("1", "str1", str1, "str2", str2);

    int res1 = compareStrings1(str1, str2);
    int res2 = compareStrings2(str1, str2);

   return 0; 
}

Output:

1.
Address of str1= 0x55bc799fea65
Address of str2= 0x55bc799fea6a

2.
Address of str1= 0x55bc799fea65
Address of str2= 0x55bc799fea6a

3.
Address of  (char **)value1= 0x55bc799fea65
Address of  (char **)value2= 0x55bc799fea6a

4.
Address of *(char **)value1= 0x3433320034333231
Address of *(char **)value2= 0x310035343332

(*str1)= 1
(*str2)= 2

5.
Address of  (char **) & value1= 0x7ffd061f56b8
Address of  (char **) & value2= 0x7ffd061f56b0

6.
Address of *(char **) & value1= 0x55bc799fea65
Address of *(char **) & value2= 0x55bc799fea6a

I hope it helps.

sg7
  • 6,108
  • 2
  • 32
  • 40