多次元配列のソートについて

解決


ZUKI-MU  2005-06-24 23:59:43  No: 58046

以下のように構造体、配列が宣言されています。

struct SYM_INFO {
  char  *name ;
  long  adr ;
  char  typ ;
} ;

SYM_INFO  sym_Tbl[3] ;

この配列 sym_Tbl に格納された値を指定された次元の要素
によってソートをかけたいと考えています。例えば

     name {CFI_KP,   B_EXTR,   DA12_GS,  ACR_OUT }
     adr  {00000CAC, 00014346, 00000B98, 000143F4}
     typ  {W,        W,        B,        D       }

という順に格納されている場合、name に対してソートをかけると

     name {ACR_OUT,  B_EXTR,   CFI_KP,   DA12_GS }
     adr  {000143F4, 00014346, 00000CAC, 00000B98}
     typ  {D,        W,        W,        B       }

という具合に、組み合わせは変わらずに変更をかけたいのです。
もとは VC++Ver1.5 でコンパイルされたプログラムでその時の処理は

int sym_number ;

/*プロトタイプ*/
static int compare_adr(const struct SYM_INFO *, const struct SYM_INFO *);

int main(int argc, char *argv[])
{
     <中略>
     qsort(sym_tbl, sym_number, sizeof(struct SYM_INFO), compare_mdf);

     <中略>
}

static int  compare_mdf(const struct SYM_INFO *k, const struct SYM_INFO *y)
{
    return(strcmp(k -> name, y -> name));
}

となっています。
現開発環境は VC++.NET であり、この環境では qsort の第四引数に関して
コンパイルエラーが発生してしまいます。関数 compare_mdf の引数の型の
問題なのは分かっているのですが、どうもうまくいきません。できれば構造体
や配列の型をかえずに処理をしたいのですが、どなたかご教授願えませんで
しょうか。よろしくお願い致します。


isshi  2005-06-25 00:08:19  No: 58047

>static int  compare_mdf(const struct SYM_INFO *k, const struct SYM_INFO *y)

static int compare_mdf(const void *k, const void *y)

とすればよいと思います。


ZUKI-MU  2005-06-25 00:47:28  No: 58048

isshiさん。早速の御回答ありがとうございます。
ただこの変更だと関数 compare_mdf 内で構造体 SYM_INFO の要素
name にアクセスできないのですが。この関数内の return 文には
どのような値を返せばよいのでしょうか?


シャノン  2005-06-25 00:49:52  No: 58049

> 関数 compare_mdf 内で構造体 SYM_INFO の要素
> name にアクセスできないのですが

キャストしませう。


ZUKI-MU  2005-06-25 01:42:15  No: 58050

シャノンさん。御回答ありがとうございます。
ただ、SYM_INFO型へのキャスト処理がどうしてもうまくいかないのですが、
どんな方法を使うのでしょうか。よろしければ教えていただきたいのですが。


Blue  2005-06-25 01:53:45  No: 58051

( const struct SYM_INFO* )k
とかでは?
# C++だと
# reinterpret_cast< struct SYM_INFO* >( k )
# かな。。。(constはどうなるんだっけ?)

> ただ、SYM_INFO型へのキャスト処理がどうしてもうまくいかないのですが、
どんなことやっているのかわからないのでソースを提示してもらえませんか?


ZUKI-MU  2005-06-25 02:06:26  No: 58052

Blueさん。御回答ありがとうございます。

static int  compare_mdf(const struct SYM_INFO *k, const struct SYM_INFO *y)
{
    return(strcmp(k -> name, y -> name));
}

static int  compare_mdf(const void *k, const void *y)

とし、内部の return文の k を

( struct SYM_INFO* )k や
static_cast< struct_SYM_INFO* >( K )

など色々試しておりました。

そしてBlueさんの御回答

reinterpret_cast< struct SYM_INFO* >( k )

で解決致しました。本当にありがとうございます。
isshiさん、シャノンさんも本当にありがとうございます。


シャノン  2005-06-25 02:51:43  No: 58053

void * から T *(T は任意の型)へのキャストは static_cast でよかったハズ。


シャノン  2005-06-25 02:56:00  No: 58054

> reinterpret_cast< struct SYM_INFO* >( k )

あと、C++ では struct キーワードは要らねぇです。
ついでに、比較関数ではメンバの値を書き換えませんから、const にします。

static_cast< const SYM_INFO * >( k )

> (constはどうなるんだっけ?)

const void * から const T * への変換は static_cast で。
const void * から const でない void * へは const_cast で。
const void * から const でない T * への変換は reinterpret_cast しかありませんが、上の2つの方法を組み合わせたほうがいいかも。
reinterpret_cast は T * と DWORD_PTR の相互変換くらいにしか使わないほうがいいです。


Blue  2005-06-25 03:19:31  No: 58055

reinterpret_castの使い方を誤認識していました。
ポインタ→ポインタもreinterpret_castだとおもっていました。

constをつけるのもstatic_castでいいのでしょうか?
(いまはconst_castだと思っている。)

ところで、
> ( struct SYM_INFO* )k や
> static_cast< struct_SYM_INFO* >( K )
>
> など色々試しておりました。
でなぜうまくいかななかったのかなぞです。


シャノン  2005-06-25 03:46:35  No: 58056

> constをつけるのもstatic_castでいいのでしょうか?

外すのには const_cast が必要ですが、つけるのはキャスト要らずで巣。

char c = 'A';
char * pc = &c;
const char * cpc = pc;
char * pc2 = const_cast< char * >( cpc );


Ban  2005-06-25 03:48:28  No: 58057

> constをつけるのもstatic_castでいいのでしょうか?
> (いまはconst_castだと思っている。)

const と volatile の除去と付加はいずれも const_cast ですが、
非 const から const には暗黙で変換できますからあまり明示しない気も。


シャノン  2005-06-25 04:27:11  No: 58058

キャストとは、プログラミングにおいてトップクラスに危険な操作です。
コンパイラが型の不一致エラーを出そうとするのを、強制的に黙らせるわけですから、バグを作りこむ可能性を孕んでいます。
できるならば、使わないに越したことはありません。

const をつけることは常に安全ですが、外すのには危険が伴います(変更不可能とされているものを変更可能にするわけですから)。
だからつけるのにはキャストが要りません。

reinterpret_cast は最も強引なキャストです。つまり、最も危険なキャストです。
void * → T * の変換や、基底クラスのポインタ → 派生クラスのポインタのキャストなどは reinterpret_cast でもできますが、static_cast でもできます。
こういう場合、より適用範囲が狭い static_cast を使うべきでしょう。
基底クラス → 派生クラスには、dynamic_cast を使うとよりよいかも。


シャノン  2005-06-25 04:43:45  No: 58059

ちと長いけどサンプル:

#include <iostream>
#include <string>
#include <exception>

using std::cout;
using std::endl;
using std::string;
using std::bad_cast;

class Base
{
public:
  virtual ~Base();
};

class Derived : public Base
{
public:
  void Hoge();
};

int main()
{
  char c = 'A';
  char * pc = &c;
  const char * cpc = pc; // 常に安全

  // プログラマには安全だとわかっている(元々 const でないものを元に戻しただけ)が、
  // コンパイラにはわからないのでキャストが必要
  char * pc2 = const_cast< char * >( cpc );
  *pc2 = 'B';

  // こういうことはコンパイルエラーにはならないが、
  // 実行時エラーになる(元々 const)のでやってはいけない。
  const char * cstr = "ABC";
  char * str = const_cast< char * >( cstr );
  *str = 'X';

  Base b;
  Derived d;

  Base * pb = &d; // 常に安全

  // プログラマには安全だとわかっている(元々 Derived * なのを元に戻しただけ)が、
  // コンパイラにはわからないのでキャストが必要
  Derived * pd = static_cast< Derived * >( pb );
  pd->Hoge();

  // こういうことはコンパイルエラーにはならないが、
  // 実行時エラーになる(Base には Hoge がない)のでやってはいけない。
  Derived * pd2 = static_cast< Derived * >( &b );
  pd2->Hoge();

  // プログラマには安全だとわかっている(元々 Derived * なのを元に戻しただけ)が、
  // コンパイラにはわからないのでキャストが必要
  Derived * pd3 = dynamic_cast< Derived * >( pb );
  
  // dynamic_cast を使うと、より安全
  // (元々変換不能なポインタを変換しようとすると、結果は NULL になる)
  Derived * pd4 = dynamic_cast< Derived * >( &b );

  Base & rb = d; // 常に安全

  // プログラマには安全だとわかっている(元々 Derived & なのを元に戻しただけ)が、
  // コンパイラにはわからないのでキャストが必要
  Derived & rd = static_cast< Derived & >( rb );
  rd.Hoge();

  // こういうことはコンパイルエラーにはならないが、
  // 実行時エラーになる(Base には Hoge がない)のでやってはいけない。
  Derived & rd2 = static_cast< Derived & >( b );
  rd2.Hoge();

  // プログラマには安全だとわかっている(元々 Derived & なのを元に戻しただけ)が、
  // コンパイラにはわからないのでキャストが必要
  Derived & rd3 = dynamic_cast< Derived & >( rb );

  try
  {
    // dynamic_cast を使うと、より安全
    // (元々変換不能な参照を変換しようとすると、例外を投げる)
    Derived & rd4 = dynamic_cast< Derived & >( b );
  }
  catch( const bad_cast & ex )
  {
    cout << ex.what() << endl;
  }

  return 0;
}


ZUKI-MU  2005-06-25 04:51:44  No: 58060

シャノンさん。多大なるご指摘感謝いたします。
先程は解決マークを付けてしまったので書きませんでしたが、
reinterpret_cast< struct SYM_INFO* >( k )
ではコンパイルが通りませんでした。
reinterpret_cast< const SYM_INFO* >( k )で通っていましたが、
ご指摘頂いた static_cast に変更させて頂きました。
シャノンさん、Blueさん、Banさん本当にありがとうございました。


シャノン  2005-06-25 04:54:29  No: 58061

> reinterpret_cast< struct SYM_INFO* >( k )
> ではコンパイルが通りませんでした。

const void * から SYM_INFO * への変換では、const を外さなければなりませんが、reinterpret_cast ではこれができないのでエラーになります。

const void * から const SYM_INFO * への変換であれば、const を外す必要は無いので、static_cast で十分です。
reinterpret_cast でもできますが、これは極力使わないほうが良いです。


DD.  2005-06-25 05:00:33  No: 58062

ちょっと横から。
シャノンさんわかりやすい解説ご苦労様です。

アップキャスト&ダウンキャストのわかりやすい例だと思います。

const_castについても勉強になります。


シャノン  2005-06-25 05:17:26  No: 58063

> アップキャスト&ダウンキャストのわかりやすい例だと思います。

ありがとうございます。
実は自分でも良くわかってなくて、こうして書いてみることですっきりしました(笑


Ban  2005-06-25 06:11:40  No: 58064

明示的に const_cast で const をつける例を考えると、
例えば const 版と非 const 版がオーバーロードされた関数で、
const 版を明示的に呼びたい場合などでしょうか。

一般的にはかなり限られた状況だと思いますが、
上記のようなオーバーロードは主に効率化のためにされるものですから、
時には効率面で有益なのかも...。


Ban  2005-06-25 06:14:08  No: 58065

# 誤操作したらしく、解決チェックが付いてました.....orz
ZUKI-MU さん、申し訳ありません。m(_ _)m


Blue  2005-06-25 06:22:23  No: 58066

Banさん、シャノン様、
どうも解説ありがとうございました。(*- -)(*_ _)ペコッ

C++のキャストはあんまり慣れていないので勉強になりました。
# 仕事では全部Cタイプのキャスト
# 特に規約もなく、周りの人に合わせる感じになっています。
# 定数も#defineも多様してますし。。。


シャノン  2005-06-25 06:37:04  No: 58067

一ヶ所嘘つきました@17:56

> const void * から const でない T * への変換は reinterpret_cast しかありませんが、上の2つの方法を組み合わせたほうがいいかも。

reinterpret_cast じゃできません。
まず const_cast で non-const void * にしてから、static_cast を使うしかないです。


YuO  2005-06-25 10:12:20  No: 58068

解決とあるものの,誰も言わなかったので一応。

qsortではなくstd::sortを使えばキャストの問題は全て解決します。
個人的には,この方法を推奨。
ref) Effective STL 第46項

#include <functional> // 追加
#include <algorithm>  // 追加

struct compare_mdf : std::binary_function<SYM_INFO, SYM_INFO, bool)
{
    bool operator() (const SYM_INFO & lhs, const SYM_INFO & rhs) const
    {
        return strcmp(lhs.name, rhs.name) < 0;
    }
};

// main内
std::sort(sym_tbl, sym_tbl + sym_number, compare_mdf());


ZUKI-MU  2005-06-28 18:59:41  No: 58069

YuOさん。御回答ありがとうございます。
以前このサイトでYuOさんが std::sort を使用した例があり
それも参考にしてやっていたのですがうまくいかなかったので、
これを見てすっきり致しました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加