C言語関係掲示板

過去ログ

No.1290 構造体型のデータをqsort()で並べ替える

[戻る] [ホームページ]
No.17135

構造体型のデータをqsort()で並べ替える
投稿者---jun(2004/10/06 16:14:11)


こんにちは。C言語をはじめて1ヶ月半の初心者です。
悩んでいることがあります。どなたか教えてくださいませ。


typedef struct seiseki {
       UINT    id;                /* ID           */
       UCHAR   name[NAME_MAX];    /* 名前         */
       USHORT  j_point;           /* 国語点数     */
       USHORT  m_point;           /* 数学点数     */
       USHORT  e_point;           /* 英語点数     */
       USHORT  t_point;           /* 三教科合計点 */
} SEISEKI;

と定義された型の各点数でソートをしたいと考えています。現在はクイックソートで関数を定義しているのですが、C言語にはqsort()という関数が存在することを知り、現在、このqsort()の実装に行き詰っています。
現在は
typedef struct seiseki_manager {
       USHORT  j_point_sum;         /* 国語点数合計           */
       USHORT  m_point_sum;         /* 数学点数合計           */
       USHORT  e_point_sum;         /* 英語点数合計           */
       USHORT  t_point_sum;         /* 三教科合計合計         */
       UINT    total;               /* 人数                   */
       SEISEKI *p;                      /* 成績データへのポインタ */
}MNG ;


という構造体を定義しており、このMNG型の変数を使って成績データを管理しています。成績データへのポインタというのが一人目の成績データを格納しているアドレスで、2人目はそれに続けて入っているので、つまり成績データの、一人目、二人目、三人目・・・・・、というのがp[0],p[1],p[2]・・・というアドレスに入っていて、p[total-1]に最後の人の成績データが入っているというかたちです。よって、現在の実装は、

#define ID 1
#define J 2
#define M 3
#define E 4
#define T 5

と、DEFINEを定義しておいて、

以下のようなクイックソートを利用した関数を2つ定義し

UCHAR  sort(MNG *mng,UCHAR type);
void   insort(SEISEKI *p,int lower,int upper,UCHAR type);


void insort(SEISEKI *p,int lower,int upper,UCHAR type)
{
    SEISEKI temp;
    int pivot;
    int i,j;
    
    
    /*番号順*/
    if(type==ID){
        pivot=p[(lower+upper)/2].id;
    }
    /*国語得点順*/
    if(type==J){
        pivot=p[(lower+upper)/2].j_point;
    }
    
    /*数学得点順*/
    if(type==M){
        pivot=p[(lower+upper)/2].m_point;
    }
    /*英語得点順*/
    if(type==E){
        pivot=p[(lower+upper)/2].e_point;
    }
    /*合計得点順*/
    if(type==T){
        pivot=p[(lower+upper)/2].t_point;
    }
    
    i=lower; 
    j=upper;
    
    for(;;)
    {
        /*番号順*/
        if(type==ID){
            while(pivot>p[i].id) i++;
            while(pivot<p[j].id) j--;
        }
        /*国語得点順*/
        if(type==J){
            while(pivot<p[i].j_point) i++;
            while(pivot>p[j].j_point) j--;
        }
        /*数学得点順*/
        if(type==M){
            while(pivot<p[i].m_point) i++;
            while(pivot>p[j].m_point) j--;
        }
        /*英語得点順*/
        if(type==E){
            while(pivot<p[i].e_point) i++;
            while(pivot>p[j].e_point) j--;
        }
        /*合計得点順*/
        if(type==T){
            while(pivot<p[i].t_point) i++;
            while(pivot>p[j].t_point) j--;
        }
        
        if(i>=j) break;
        
        temp=p[i];
        p[i]=p[j];
        p[j]=temp;
        
        i++;
        j--;
        
    }
    /*再帰呼び出し*/
    
    if(lower<i-1) insort(p,lower,i-1,type);
    if(j+1<upper) insort(p,j+1,upper,type);
}

UCHAR  sort(MNG *mng,UCHAR type){
    insort(mng->p,0,mng->total-1,type);
}





MNG mng;


上記のように定義された変数で成績データを管理しているとすると

国語の点でソートしたければ、

sort(&mng,type);



などと利用する感じです。

これは問題なく動作しています。

しかしながら、人に見せられるようなソースでないことは自分でも自覚しています、

void   insort(SEISEKI *p,int lower,int upper,UCHAR type);


のなかのif分をどうにかスマートにしたいと思って、たどりついたのがqsort()なのですが、第四引数の関数へのポインタというのが理解できないのでどうにも進めないのです。WEBで調べてみたのですがいまいちぐっと来るのが見つかりませんでした。誰か教えてください!!多分は私は関数ポインタというものが何なのかというとこから理解できていないと思います。また、現在の問題をもっと簡単に解消できるという何か良い案があれば合わせて教えていただければ幸いです。

ではよろしくお願いします。



No.17138

Re:構造体型のデータをqsort()で並べ替える
投稿者---Sciggepy変化前(2004/10/06 17:10:38)


キー(教科)別に比較関数を作って、使い分けます。
比較関数は、<pre>
int comp(const void *p1,const void *p2);
//p1,p2: 配列中のデータへのポインタが格納されます。
<pre>qsort(p,upper,sizeof(SEISEKI),comp);とすると、compでは、
((const SEISEKI *)p1)->IDのように値を取得できます。
負の値を返せばp2→p1の順に、正の値を返せばp1→p2の順に並びます。


No.17140

Re:構造体型のデータをqsort()で並べ替える
投稿者---jun(2004/10/06 17:39:20)


ありがとうございます。やってみます。


No.17155

Re:構造体型のデータをqsort()で並べ替える
投稿者---jun(2004/10/07 09:59:56)


やってみました。しかしエラーが・・・。

比較関数を

UINT id_comp(const void *a,const void *b){
    if((const SEISEKI *)a->id < (const SEISEKI *)b->id){
        return -1;
    }
    else if((const SEISEKI *)a->id > (const SEISEKI *)b->id){
        return 1;
    }
    else{
        return 0;
    }
}


このように定義して、メインで

MNG mng;


のように定義しているの変数で成績データを管理しているので

qsort(mng.p,mng.total,sizeof(SEISEKI),id_comp);


のように使っているのですが、

warning: dereferencing `void *' pointer
error: request for member `id' in something not a structure or unio
n

などというエラーが発生します。

以前書き忘れたのですが、

#define UINT unsigned int
#define UCHAR unsigned char
#define USHORT unsigned short
#define FLOAT float

のように定義もしています。

どこか間違っているでしょうか??

なお、コンパイルの環境はcygwin上のgccです。
お願いいたします。


No.17156

Re:構造体型のデータをqsort()で並べ替える
投稿者---REE(2004/10/07 10:10:15)


>どこか間違っているでしょうか??

演算子の優先順位の問題です。
下記のようにして下さい。

((const SEISEKI *)a)->id



No.17157

Re:構造体型のデータをqsort()で並べ替える
投稿者---jun(2004/10/07 10:40:23)


ありがとうございました。
なんとか解決し実行できました。

ここでもうひとつ疑問なんですが、qsort関数の第四引数の関数へのポインタがありますが、この比較関数は必ずintを返す関数でないといけないのでしょうか??

私の場合、SEISEKIという構造体で点数をunsigned shortで定義しているので比較関数の戻り値もunsigned shortにしていたらうまくソートが行われませんでした。まぁintにすれば問題なく動作しましたが、気になります。


No.17158

Re:構造体型のデータをqsort()で並べ替える
投稿者---REE(2004/10/07 11:35:50)


>ここでもうひとつ疑問なんですが、qsort関数の第四引数の関数へのポインタがありますが、この比較関数は必ずintを返す関数でないといけないのでしょうか??

はい、そうです。

>私の場合、SEISEKIという構造体で点数をunsigned shortで定義しているので比較関数の戻り値もunsigned shortにしていたらうまくソートが行われませんでした。まぁintにすれば問題なく動作しましたが、気になります。

この関数の戻り値は比較結果ですので、比較する値の型は関係ありません。
例えば、文字列で比較する場合に、結果を文字列で返すのは変ですよね?



No.17160

Re:構造体型のデータをqsort()で並べ替える
投稿者---jun(2004/10/07 13:08:33)



>この関数の戻り値は比較結果ですので、比較する値の型は関係ありません。
>例えば、文字列で比較する場合に、結果を文字列で返すのは変ですよね?

そうですね・・・。ほんとだ。完璧に勘違いしてました。
比較する値の型がもしもint型なら、比較関数の中身を

typedef struct seiseki {
       int    id;                /* ID           */
       char   name[NAME_MAX];    /* 名前         */
       short  j_point;           /* 国語点数     */
       short  m_point;           /* 数学点数     */
       short  e_point;           /* 英語点数     */
       short  t_point;           /* 三教科合計点 */
} SEISEKI;



ならば、

int id_comp(const void *a,const void *b){
    
    SEISEKI *u,*v;
    u=(SEISEKI *)a; 
    v=(SEISEKI *)b;
    return (u->id) - (v->id);
}


のように簡略的にかけるというメリットがあるくらいなんですね。
理解しました。どうもありがとうございました。





No.17166

Re:構造体型のデータをqsort()で並べ替える
投稿者---REE(2004/10/07 14:21:04)


unsigned short でも

>int id_comp(const void *a,const void *b){
    
    SEISEKI *u,*v;
    u=(SEISEKI *)a; 
    v=(SEISEKI *)b;
    return (int)(u->id) - (int)(v->id);
}

とでもすれば、同様にかけます。




No.17167

Re:構造体型のデータをqsort()で並べ替える
投稿者---jun(2004/10/07 14:25:29)


そうですね。
そのように実装します。
ありがとうございました。