Click here to Skip to main content
Click here to Skip to main content

Using qsort on arrays of sequential data

By , , 26 Jan 2000
 
  • Download demo project - 14 Kb
  • Introduction

    qsort is part of the C and C++ language. It is a quick sort algorithm that is both fast and easy to implement. It is a recursive algorithm, but it is surprising fast. (30,000 items in about a fourth of a second on a PII-333 machine.)

    This 'How To' demonstrates the use of qsort on a CArray. However, it would work just as well on any other sequential data structure. Static arrays, CStringArray, CPtrArray or something of your own creation.

    The demo project is written in VC 5.0. It has not been plagiarized from any other source. There are no restrictions on the source.

    Sorting

    Suppose you wanted to store and sort number of items of type CArrayClass, defined as follows:
    class CArrayClass  
    {
    public:
    	CArrayClass();
    	virtual ~CArrayClass();
    	
    	WORD    m_wMsgId;
    	CString m_strMsgType;
    };
    

    Using the CArray template class we can store the CArrayClass objects using the following array declaration

    typedef CArray<CArrayClass, CArrayClass&> Type_aCArrayClass;
    Type_aCArrayClass m_aCArrayClass;
    

    To use qsort you need to declare a callback function whose address is known at compile time - meaning it must either be a global or static function. The qsort callback function must be defined as:

    int (__cdecl *compare )(const void *elem1, const void *elem2 )

    Items elem1 and elem2 are pointers to two items in the array, and must be cast to the appropriate type and compared. The return value of the function is as follows:

    Return Value Description
    < 0 elem1 less than elem2
    0 elem1 equivalent to elem2
    > 0 elem1 greater than elem2

    In our case the callback function would look like the following:

    //This function can be global or it can be a static member of your class,
    //but it's address must be know at compile time.
    int CCArrayExampleDlg::SortTheArray(void* p1,void* p2)
    {
    	short n=0;
    	CArrayClass* a1 = (CArrayClass*)p1;
    	CArrayClass* a2 =  (CArrayClass*)p2; // If we were sorting a CPtrArray then we 
    	                                     // would have to do a second level 
    	                                     // of casting to get to the data keys that we 
    	                                     // would be sorting.
    
    	if (a1->m_strMsgType  <a2->m_strMsgType)	//Ascending
    		n = -1; 
    	elseif (a1->m_strMsgType  >a2->m_strMsgType)
    		n = 1; 
    	elseif(a1->m_wMsgId  <a2->m_wMsgId)	//Ascending
    		n = -1; 
    	elseif(a1->m_wMsgId  >a2->m_wMsgId)
    		n = 1;
    
    	return n;
    }
    

    To use this sort callback we need to fill the array, call qsort and then display the results.

    void CCArrayExampleDlg::OnSort() 
    { 
    	// Fill the array m_aCArrayClass with items of type CArrayClass 
    	...
    
    	// Sort the array 
    	if(m_aCArrayClass.GetSize() >  0) 
    	{
    		qsort((void*)&m_aCArrayClass[0],
    		      (size_t)m_aCArrayClass.GetSize(), 
    		      sizeof(CArrayClass),
    		      (int(*)(const void*, const void*))CCArrayExampleDlg::SortTheArray);
    	}
    	
    	// Display the newly sorted array
    	...
    }

    License

    This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

    About the Authors

    Chris Maunder
    Founder CodeProject
    Canada Canada
    Member
    Chris is the Co-founder, Administrator, Architect, Chief Editor and Shameless Hack who wrote and runs The Code Project. He's been programming since 1988 while pretending to be, in various guises, an astrophysicist, mathematician, physicist, hydrologist, geomorphologist, defence intelligence researcher and then, when all that got a bit rough on the nerves, a web developer. He is a Microsoft Visual C++ MVP both globally and for Canada locally.
     
    His programming experience includes C/C++, C#, SQL, MFC, ASP, ASP.NET, and far, far too much FORTRAN. He has worked on PocketPCs, AIX mainframes, Sun workstations, and a CRAY YMP C90 behemoth but finds notebooks take up less desk space.
     
    He dodges, he weaves, and he never gets enough sleep. He is kind to small animals.
     
    Chris was born and bred in Australia but splits his time between Toronto and Melbourne, depending on the weather. For relaxation he is into road cycling, snowboarding, rock climbing, and storm chasing.

    Phil McGahan
    United States United States
    Member
    No Biography provided

    Sign Up to vote   Poor Excellent
    Add a reason or comment to your vote: x
    Votes of 3 or less require a comment

    Comments and Discussions

     
    You must Sign In to use this message board.
    Search this forum  
        Spacing  Noise  Layout  Per page   
    GeneralPointer on CPtrArraymemberrepsac25 Aug '04 - 20:09 
    Nice article about the qsort which works great. I had some trouble sorting a CPtrArray - hence this post. Add the cast to a pointer-to-pointer as shown below:
    // file is the class/struct/whatever
    // contained in the CPtrArray
    file** ppFileTmp1 = (file**)p1;
    file** ppFileTmp2 = (file**)p2;

    file* q1 = *ppFileTmp1;
    file* q2 = *ppFileTmp2;
    GeneralRe: Pointer on CPtrArraysussBorislav Sabev17 Jan '05 - 2:49 
    I had the same problems, and resolved like this:
     
    the call to sort:
    qsort((void*)m_array.GetData(), GetCount(), sizeof(CMyObj*), CompareObjects);
    compare function:
    int __cdecl CompareObjects (const void* pelement1, const void* pelement2 )
    {
    CMyObj* p1 = *((CProjSchedObj**) pelement1);
    CMyObj* p2 = *((CProjSchedObj**) pelement2);
     
    int result = 0;
    if( p1->GetDateTime() < p2->GetDateTime() )
    result = -1;
    else
    if( p2->GetDateTime() < p1->GetDateTime() )
    result = 1;
    return result;
    }
     
    Works perfectly. Thanks a lot to all.

    GeneralRe: Pointer on CPtrArraymemberFlaviu218 Mar '12 - 9:02 
    Thank you for the code, I use it.
    GeneralI like this code...memberJonathan Craig7 Mar '02 - 4:25 
    I like qsort(). It is very easy to use. Works great for simple sorts. I would only make one change to the code in the article. And it's nothing big, but since the sorting function will get called recursively, I like to make it as clean as possible.
    int CCArrayExampleDlg::SortTheArray(CArrayClass* p1, CArrayClass* p2)
    {
      if (p1->m_strMsgType < p2->m_strMsgType)  //Ascending
        return -1; 
      elseif (p1->m_strMsgType > p2->m_strMsgType)
        return 1; 
      elseif(p1->m_wMsgId < p2->m_wMsgId)  //Ascending
        return -1; 
      elseif(p1->m_wMsgId > a2->m_wMsgId)
        return 1;
    }
    I just got rid of the locals. They get pushed on the stack each time the function gets called and eatup more stack space if it gets recursively called enough.
     
    Of course with Windows and larger stacks this may not even be a problem. I do remember the DOS days when you had to keep an eye on stuff like that.
     
    Just a comment. Smile | :)

     
    Jonathan Craig
    www.mcw-tech.com
    GeneralProblem with similar code when sorting CStringArraysusschris27 Jun '00 - 11:45 
    int CompareAscending(const void *a, const void *b)
    {
    CString *pA = (CString*)a;
    CString *pB = (CString*)b;
    return (pA->Compare(*pB));
    }
     

    void SortStringArray (CStringArray& csa, BOOL bDescending, BOOL bNoCase)
    {
    int iArraySize = csa.GetSize();
    if (iArraySize <= 0)
    return;
     
    int iCSSize = sizeof (CString*);
    void* pArrayStart = (void *)&csa[0];
     
    qsort (pArrayStart, iArraySize, iCSSize, CompareAscending);
    }
    }
     

    this code fails when size < 8 (i'm testing with size = 3), when MS's qsort routine uses "shortsort" instead...
     
    any ideas
    GeneralRe: Problem with similar code when sorting CStringArraymemberMcGahanFL18 May '03 - 7:25 
    Your problem is based in what you are passing in. Look at this example.
     
    CStringArray astr;
    CString strVar;
    //load your CStringArray
    for(int x = 0; x < 100; x++)
    {
    strVar.Format("X%d", x);
    astr.Add(strVar);
    strVar.Format("AAAaaaaaaa%d", x);
    astr.Add(strVar);
    }
     
    //sort using the sizeof a CString not the size of a
    //CString pointer
    qsort((void*)&astr[0],(size_t)astr.GetSize(),sizeof(CString),
    (int(*)(const void*,const void*))CompareAscending);
     
    //display your results
    for(int y = 0; y < astr.GetSize(); y++)
    {
    TRACE("%s\n", astr[y]);
    }

    Generalqsort is not a good choice in C++sussWilliam Kempf28 Jan '00 - 4:05 
    The qsort function is C based, and so isn't typesafe. The C++ algorithm std::sort will usually outperform qsort and is typesafe, not to mention much easier to use
    GeneralRe: qsort is not a good choice in C++memberMichael Cowperthwaite5 Jul '01 - 10:32 
    If the coder is already stuck using the MFC collection classes, it makes little sense to drag in std just for sorting.
    GeneralRe: qsort is not a good choice in C++memberJean-Marc16 Jan '02 - 23:33 
    well I don't think so. there's no real algorithm in the mfc collection. and I am trying to find a way to use STL and MFC properly with VC++ 6... sounds tricky ^^

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

    Permalink | Advertise | Privacy | Mobile
    Web04 | 2.6.130523.1 | Last Updated 27 Jan 2000
    Article Copyright 2000 by Chris Maunder, Phil McGahan
    Everything else Copyright © CodeProject, 1999-2013
    Terms of Use
    Layout: fixed | fluid