qsort 사용 예
실제 이것을 사용할 때는 이미 작성되어 라이브러리로 제공하는 함수를 사용하면 된다. 기본적인 리얼타임 라이브러리이다. 따라서 어떻게 호출하는가만 알면 사용은 쉽다.
qsort( (void *)데이터의 시작 주소, (size_t) 리스트의 갯수, 바꿀 데이터의 길이, 비교함수 );
데이터는 여러 변수의 형태를 사용할 수 있다. 따라서 소트의 길이 및 비교는 모두 다르다. 이것을 처리하기 위해 위와 같은 함수의 모양을 갖는다.
- 데이터의 시작 주소 : 데이터의 형태는 소트할 상황에 따라 다르므로 데이터가 존재하는 시작 주소를 넣으면 된다. int 일수도 있고, struct 일수도 있다. 따라서 이것은 void 포인트 변수로 타입 변환하여 넣어주면 된다.
- 리스트의 갯수 : 소트해야할 데이터의 갯수를 넣는다.
- 바꿀 데이터의 길이 : 데이터의 상황에 따라 변수의 형태와 길이가 다르므로 가급적 포인터등을 사용하려 교환하는 길이를 줄이는 것이 소트 시간을 단축할 수 있을 것이다.
- 비교함수 : 두 개의 데이터 엘리먼트를 비교하는 함수를 작성하고 함수 포인터로 넣어 주면, sort 과정 상 비교할 때 호출 된다. 이때 역시 데이터의 형태를 모르므로 void 포인터로 넘어 온다. 비교할 2개의 데이터의 위치를 포인터로 받는다.
#include <stdlib.h>
#include <string.h>
//#include <conio.h>
#include <stdio.h>
struct student {
char name[20];
char tel[20];
char id[15];
char stid[10];
char part[20];
char etc[100];
};
struct student gStudent[] = {
{ "홍길동", "010-3234-2344", "840305-1373848", "200341056", "전자공학과", "" },
{ "김홍영", "010-3344-2675", "840505-1365648", "200341057", "전자공학과", "" },
{ "박수만", "010-5234-4644", "840305-1453848", "200341066", "전자공학과", "" },
{ "송석만", "011-564-1345", "640305-1375468", "200351056", "컴퓨터공학과", "" },
{ "전승혁", "010-3344-2334", "340305-1566648", "200351012", "컴퓨터공학과", "" },
{ "김만수", "016-764-5664", "740311-1454568", "200351016", "컴퓨터공학과", "" },
};
struct student *gpStudent[] = {
&gStudent[0],
&gStudent[1],
&gStudent[2],
&gStudent[3],
&gStudent[4],
&gStudent[5]
};
#define SZ_STUDENT sizeof(gStudent)/sizeof(gStudent[0])
int compareName( const void *arg1, const void *arg2 )
{
return strcmp( ((struct student *)arg1)->name, ((struct student *)arg2)->name);
}
int compareSiId( const void *arg1, const void *arg2 )
{
return strcmp( (*(struct student **)arg1)->stid, (*(struct student **)arg2)->stid);
}
int main(int argc, char* argv[])
{
qsort( (void *) &gStudent, (size_t)SZ_STUDENT, sizeof(struct student), compareName);
int cnt;
printf("구조체에서 이름으로...\n");
for (cnt = 0;cnt < SZ_STUDENT;cnt++) {
printf("[%s] %s - %s\n", gStudent[cnt].stid, gStudent[cnt].name, gStudent[cnt].tel );
}
printf("\n포인터에서 학번으로...\n");
qsort( (void *) gpStudent, (size_t)SZ_STUDENT, sizeof(struct student*), compareSiId);
for (cnt = 0;cnt < SZ_STUDENT;cnt++) {
printf("[%s] %s - %s\n", gpStudent[cnt]->stid, gpStudent[cnt]->name, gpStudent[cnt]->tel );
}
//getch();
return 0;
}
실행을 하면 :
구조체에서 이름으로...
[200351016] 김만수 - 016-764-5664
[200341057] 김홍영 - 010-3344-2675
[200341066] 박수만 - 010-5234-4644
[200351056] 송석만 - 011-564-1345
[200351012] 전승혁 - 010-3344-2334
[200341056] 홍길동 - 010-3234-2344
포인터에서 학번으로...
[200341056] 홍길동 - 010-3234-2344
[200341057] 김홍영 - 010-3344-2675
[200341066] 박수만 - 010-5234-4644
[200351012] 전승혁 - 010-3344-2334
[200351016] 김만수 - 016-764-5664
[200351056] 송석만 - 011-564-1345
이 프로그램에서 쏘트의 방법상 다음 2가지 방법을 생각해 본다.
(1) qsort( (void *) &gStudent, (size_t)SZ_STUDENT, sizeof(struct student), compareName);
(2) qsort( (void *) gpStudent, (size_t)SZ_STUDENT, sizeof(struct student*), compareSiId);
1번은 struct 전부를 바꾸어야 하고
2번은 struct 포인터만을 바꾼다.
이렇게 되면 2가지의 쏘트에서 실행 시간이 다르게 된다. 비교 후 데이터의 교환 조건이 되면 교환을 수행할 때
- 1번 : struct의 전 사이즈를 수행 한다. 따라서 sizeof(struct student) 바이트 수 만큼 교환 된다.
- 2번 : 단순히 포인터 4바이트(32비트 CPU에서)만을 바꾸면 끝이다.
따라서 데이터 교환을 줄이려면 이런 경우 포인터가 유용하다.
프로그램 예
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <conio.h>
char *msg[] = {
"Hello",
"홍길동",
"우리집에",
"computer",
"TV",
"12345"
};
int compareAlpha( const void *arg1, const void *arg2 );
int compareLength( const void *arg1, const void *arg2 );
void printList(char **msg, int szlist);
int main(int argc, char* argv[])
{
int szlist;
szlist = sizeof(msg) / sizeof(msg[0]);
printf("원래 리스트...\n");
printList(msg, szlist);
// 알파벳 순서 소트
qsort( (void *)msg, (size_t)szlist, sizeof( char * ), compareAlpha );
printf("\n알파벳 순서 소트 결과...\n");
printList(msg, szlist);
// 문자의 길이 순서 소트
qsort( (void *)msg, (size_t)szlist, sizeof( char * ), compareLength );
printf("\n문자의 길이 순서 소트 결과...\n");
printList(msg, szlist);
getch();
return 0;
}
void printList(char **msg, int szlist)
{
int cnt;
for (cnt = 0;cnt < szlist;cnt++)
printf("[%d] %s\n", cnt+1, msg[cnt]);
}
// 알파벳 순서 비교
int compareAlpha( const void *arg1, const void *arg2 )
{
return _stricmp( * ( char** ) arg1, * ( char** ) arg2 );
}
// 문자의 길이 비교
int compareLength( const void *arg1, const void *arg2 )
{
return strlen( *( char** ) arg1) - strlen ( *( char** ) arg2 );
}
결과 :
원래 리스트...
[1] Hello
[2] 홍길동
[3] 우리집에
[4] computer
[5] TV
[6] 12345
알파벳 순서 소트 결과...
[1] 12345
[2] computer
[3] Hello
[4] TV
[5] 우리집에
[6] 홍길동
문자의 길이 순서 소트 결과...
[1] TV
[2] Hello
[3] 12345
[4] 홍길동
[5] 우리집에
[6] computer
올림순, 내림순하는 경우의 비교는 다음과 같은 비교함수를 바꾸면 된다.
// 알파벳 순서 비교
int compareAlpha2( const void *arg1, const void *arg2 )
{
return _stricmp( * ( char** ) arg2, * ( char** ) arg1 );
}
[1] 홍길동
[2] 우리집에
[3] TV
[4] Hello
[5] computer
[6] 12345
// 문자의 길이 비교
int compareLength2( const void *arg1, const void *arg2 )
{
return strlen( *( char** ) arg2) - strlen ( *( char** ) arg1 );
}
[1] computer
[2] 우리집에
[3] 홍길동
[4] Hello
[5] 12345
[6] TV
Linux에서 예
Linux man page
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
static int
cmpstringp(const void *p1, const void *p2)
{
/* The actual arguments to this function are "pointers to
pointers to char", but strcmp() arguments are "pointers
to char", hence the following cast plus dereference */
return strcmp(* (char * const *) p1, * (char * const *) p2);
}
int
main(int argc, char *argv[])
{
int j;
assert(argc > 1);
qsort(&argv[1], argc - 1, sizeof(char *), cmpstringp);
for (j = 1; j < argc; j++)
puts(argv[j]);
exit(EXIT_SUCCESS);
}