0

I tried sorting my environment.

My C++ version (the std::sort line along with the CstrLess class) works, but the qsort version fails. What am I doing wrong?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
extern char** environ;


struct CstrLess{
  bool operator()(const char* s1, const char* s2){ return ::strcmp(s1,s2)<0; }
};

int main(){
  char** env = environ;
  size_t sz = 0;
  for(;*env; env++,sz++) {;} //measure the env

  //?
  qsort(environ, sz, sizeof(char*), (int (*)(const void*, const void*)) strcmp);

  /*std::sort(environ, environ + sz, CstrLess{});*/

  env = environ;
  while(*env){
    printf("%s%c", *env++, '\0');
  } 

  return 0;
}
3
  • What do you mean by "fails"? Commented May 3, 2016 at 10:47
  • qsort pass char** to strcmp, not char*. Commented May 3, 2016 at 10:48
  • @ecatmur The output isn't sorted. Commented May 3, 2016 at 10:50

2 Answers 2

2

qsort passes its comparison function pointers to the elements to be compared; you need to indirect that pointer to get the char const* pointers to be passed to strcmp:

qsort(environ, sz, sizeof(char*),
    [](const void* a, const void* b) {
        return strcmp(
            *reinterpret_cast<const char**>(a), *reinterpret_cast<const char**>(b)); });

Pure C solution:

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

// ...

  qsort(environ, sz, sizeof(char*), qsort_strcmp);
Sign up to request clarification or add additional context in comments.

2 Comments

Why the cast to const char** btw? Seems entirely superfluous.
@Lundin the pointer has to be indirected, as well as cast. You can't indirect a const void *, as that would yield a const void lvalue, which can't exist.
1

A pointer to strcmp is of type int(*)(const char *, const char *).

The type expected by qsort is int(*)(const void*, const void*).

Wild function pointer conversions is undefined behavior. Your compiler would rightfully have given you a warning/error if you hadn't silenced it with the ugly cast. It is possible that there could be different calling conventions for the different types, so this undefined behavior is a valid concern.

The correct approach is to write a wrapper function and pass a pointer to that instead:

int strcmp_wrapper (const void* s1, const void* s2)
{
  return strcmp(s1, s2); // implicit conversion from const void* to const char*.
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.