简体   繁体   中英

Proper way to do a pointer-to-string in a qsort function

Let's say I have an array of strings I want to sort, such as:

{"one", "two", "three", "four"};

Using qsort what would be the proper cast operation when passing those strings?

int scmp(const void *p1, const void *p2) {
    // string1 = ???   
};

My thought is the item would be a "pointer-to-string", and since a string is a "pointer-to-char", this would give me a "pointer-to-pointer-char". Is the following the correct way to do this?

// 1. Pointer-to String
(String) *

// 2. String = Pointer-to-char
(char*) *

// 3. What about const-ness? Doesn't qsort require the 'value' to be const? 
//    Also, what about string = unsigned char -- is that important here?
(const char*) *

So, would the proper definition of string1 be:

int scmp(const void *p1, const void *p2) {
    const char* string1 = (const char**) p1;
};

Is the the correct way to do it? You can see how tedious (for me at least) it is to figure out what the pointer declarations (or cast in this case) is. Is there a more straightforward way to do it -- that is, a way where if I know I want a "pointer-to-string" I can do it (in the proper way, with const and unsigned or whatever) in less than five minutes?

qsort provides the comparison function with two const pointers to the elements of the array begin sorted. You're sorting an array of "strings", which is to say an array of char* . An array of char* is char*[] , a pointer to an element in such an array is a pointer to char* , which would be char** , and a const pointer to an element in that array would be char*const* .

You'll probably end up using strcmp to compare the two strings, or something similar. If it were strcmp itself, you would end up with:

int scmp(void const* va, void const* vb) {
  const char* a = *(char* const*)va;
  const char* b = *(char* const*)vb;
  return strcmp(a, b);
}

I added unnecessary but harmless const qualifiers to a and b , because strcmp doesn't mutate its arguments. Other than that, the example code is pretty well the same as what you would find if you looked at the example code in man qsort

It's kind of messy because of how qsort() uses pointers to elements , so if the elements themselves are pointers then you need to somehow convert void* into char* but also de-reference it one step along the way.

Here's an example of how you'd write such a function:

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

And a test program to show how it works:

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


int main(void) {
  char *list[] = {
    "boba",
    "dada",
    "zapa",
    "cobb",
    "acdc",
    "abba"
  };

  int list_len = sizeof(list) / sizeof(char*);

  qsort(&list[0], list_len, sizeof(char*), qs_strcmp);

  for (int i = 0; i < list_len; ++i) {
    printf("%s\n", list[i]);
  }

  return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM