1

Here is the code:

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

int my_compare(const void * a, const void * b);

int main()
{
    char s[][80] =
        { "gxydyv", "gdyvjv", "lfdtvr", "ayfdbk", "sqkpge", "axkoev", "wdjitd", "pyrefu", "mdafyu",
 "zdgjjf", "awhlff", "dqupga", "qoprcn", "axjyfb", "hfrgjf", "dvhhhr" };
    int i;
    puts("#Before:#");
    for (i = 0; i < 16; i++)
        puts(s[i]);
    qsort(s, 16, sizeof *s, my_compare);
    putchar('\n');
    puts("#After:#");
    for (i = 0; i < 16; i++)
        puts(s[i]);
    return 0;
}

int my_compare(const void *a, const void *b)
{
    return strcmp(*(char **)a, *(char **)b);
}

Here is the output:

#Before:#
gxydyv
gdyvjv
lfdtvr
ayfdbk
sqkpge
axkoev
wdjitd
pyrefu
mdafyu
zdgjjf
awhlff
dqupga
qoprcn
axjyfb
hfrgjf
dvhhhr
Segmentation fault

I also notice that the prototype of strcmp is: int strcmp(const char *s1,const char *s2);

I suppose that the type of a and b in my_compare is "pointer to array-of-char". As a result, *(char **)a is a "pointer to char", which is exactly what strcmp expects.

So where is the problem?

nalzok
  • 14,965
  • 21
  • 72
  • 139
  • `qsort(s, 16, sizeof *s, my_compare);` and `int my_compare(const void *a, const void *b)` or to eliminate hardcoding values: `qsort(s, sizeof s/sizeof *s, sizeof *s, my_compare);` – David C. Rankin Jan 02 '16 at 03:12
  • Is it me or did the original question try to loop through 80 values when there were significantly fewer? My mind and eyes might be playing tricks though. – David Hoelzer Jan 02 '16 at 03:15
  • You are correct -- the original questions keeps changing... – David C. Rankin Jan 02 '16 at 03:17

2 Answers2

4

Change:

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

To:

return strcmp(a,b);

You had an extra level of pointer dereferencing that was incorrect and that's why you got the segfault. That is, you were passing the char values and not the char pointers [which got masked with the cast].

Note: no need to cast from void * here.


UPDATE:

In reponse to your question, yes, because of the way you defined s and the qsort call.

Your original my_compare would have been fine if you had done:

char *s[] = { ... };

And changed your qsort call to:

qsort(s, 16, sizeof(char *), my_compare);

To summarize, here are two ways to do it

int
main()
{
    char s[][80] = { ... }

    qsort(s, 16, 80, my_compare);

    return 0;
}

int
my_compare(const void *a, const void *b)
{
    return strcmp(a,b);
}

This is a bit cleaner [uses less space in array]:

int
main()
{
    char *s[] = { ... }

    qsort(s, 16, sizeof(char *), my_compare);

    return 0;
}

int
my_compare(const void *a, const void *b)
{
    return strcmp(*(char **) a,*(char **) b);
}

UPDATE #2:

To answer your second question: No

None of these even compile:

return strcmp((char ()[80])a,(char ()[80])b);
return strcmp(*(char ()[80])a,*(char ()[80])b);
return strcmp((char [][80])a,(char [][80])b);
return strcmp(*(char [][80])a,*(char [][80])b);

But, even if the did, they would be logically incorrect. The following does not compile either, but is logically closer to what qsort is passing:

return strcmp((char [80])a,(char [80])b);

But, when a function passes something defined as char x[80] it's just the same as char *x, so qsort is passing char * [disguised as void *].

A side note: Using char *s[] is far superior. It allows for arbitrary length strings. The other form char s[][80] will actually fail if a given string exceeds [or is exactly] 80 chars.


I think it's important for you to understand:

  1. Arrays are call by reference.
  2. The interchangeability of arrays and pointers.

The following two are equivalent:

char *
strary(char p[])
{

    for (;  *p != 0;  ++p);

    return p;
}

char *
strptr(char *p)
{

    for (;  *p != 0;  ++p);

    return p;
}

Consider the following [outer] definitions:

char x[] = { ... };
char *x = ...;

Either of these two may be passed to strary and/or strptr in any of the following forms [total of 20]:

strXXX(x);
strXXX(x + 0);
strXXX(&x[0]);

strXXX(x + 1);
strXXX(&x[1]);

Also, see my recent answer here: Issue implementing dynamic array of structures

Craig Estey
  • 30,627
  • 4
  • 24
  • 48
  • You mean the a and b given to my_compare by qsort are pointers to char, other than pointers to array-of-char? – nalzok Jan 02 '16 at 03:24
  • For char s[][80], maybe you should use int my_compare(const void *a, const void *b) { return strcmp(*(char (*)[80])a, *(char (*)[80])b); } – nalzok Jan 02 '16 at 10:01
0

You can just cast it to a const char *, it should work now:

int my_compare(const void *a, const void *b) {
    return strcmp((const char *)a, (const char *)b);
}

And also you should add:

#include <stdlib.h>
DZDomi
  • 1,685
  • 15
  • 13