• ベストアンサー

qsortの引数について

以下のプログラムがあります。 int compare( const char **name1, const char **name2 ) { return strcmp( *name1, *name2 ); } int main( void ) { char *names[] = { "rand", "calloc", "malloc" }; int num = sizeof names / sizeof names[0]; qsort( names, num, sizeof( names[0] ), (int (*)(const void *, const void * ))compare ); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~この部分 return 0; } 上の「~~~」の上の部分のqsort関数の第4引数のキャストの意味が 分かりません。なぜ関数の前に引数が書かれるのでしょうか? またintの後にある(*)は「int型のポインタ」と言う意味なのか、 compare関数のポインタなのかも分かりません。 ご回答よろしくおねがいします。

質問者が選んだベストアンサー

  • ベストアンサー
  • noocyte
  • ベストアンサー率58% (171/291)
回答No.4

> int (*compare)(const void *, const void * )) > 普通の関数のポインタ宣言のようにではダメなのでしょうか・・・ ↑これは compare という変数の型を宣言 (定義) するのが目的ですが, qsort の第4引数では,ある型 (F1 型とする) の関数へのポインタを 別の型 (F2 とする) の関数へのポインタにキャストするのが目的です. したがって,F2 型の関数や変数の宣言ではなく,F2 型そのものの名前 (というか宣言) が必要になります.これは,「変数の型宣言から変数名 を除いたもの」と考えるとわかりやすいと思います. 例えば,変数の型宣言 (定義) char *string; から変数名 string を除くと char* が残るので,string の型(名) は char* 型です. 同様に関数の宣言 int (*compare)(const void *p1, const void *p2); から関数名 compare (および引数名) を除くと int(*)(const void*, const void*) が残るので,compare の型(名)は int(*)(const void*, const void*) 型 (上記の F2 型) です. このように,型そのものの宣言を抽象型宣言 (abstract type declaration) といいます. 日本語で "抽象型宣言" で検索してもわずかしかヒットしませんね…. Google 検索:「+"抽象型宣言"」 http://www.google.co.jp/search?sourceid=navclient-ff&ie=UTF-8&rls=GGGL,GGGL:2006-34,GGGL:ja&q=%2B%22%E6%8A%BD%E8%B1%A1%E5%9E%8B%E5%AE%A3%E8%A8%80%22 Google 検索:「+"abstract type declaration"」 http://www.google.co.jp/search?q=%2B%22abstract+type+declaration%22&hl=ja&lr=&rls=GGGL,GGGL:2006-34,GGGL:ja&start=30&sa=N

goo-ts
質問者

お礼

なるほど! そう考えると理解できます。 分かりやすいご説明ありがとうございました。 しかしこれについて書いてるサイトってほとんど無いに等しいのですね・・・

その他の回答 (6)

回答No.7

再び#6です。 #6の方針でqsort()呼び出しを書き直してみましょう。 -- qsort(names, num, sizeof(names[0]), compare); -- シンプルですね。ではcompare()も書き直しましょう。 -- int compare(const void * p1, const void * p2) { const char ** name1 = (const char **) p1; const char ** name2 = (const char **) p2; return strcmp(*name1, *name2); } -- このようにシンプルなときはname1, name2が無駄に見えるかもしれませんが、ある程度複雑な関数ならこの方が見易いでしょう。 #或いはconst char **版の関数を別に用意して、それを呼び出してもいいでしょう。 さて、ポインタの扱いについて簡単に説明しておきます。 ソート対象物は、intの配列のときは勿論intです。処が今回のnamesの場合はchar *の配列ですね。つまり、char *がソート対象です。 intを指すポインタはint *ですから、当然char *を指すポインタはchar **になります。 逆に考えると、char *ではcharしか指せませんから、文字列のソートではなく文字のソートしかできないことになってしまうわけです。

goo-ts
質問者

お礼

ありがとうございます。 よく分かりました。

回答No.6

compare()のプロトタイプをqsort()の要求する型に合わせてやれば、 その無意味なキャストをしないで済むのですけどね。 その場合は、compare()内部でconst void *をconst char **にキャストすればいいだけですから。

goo-ts
質問者

お礼

compare内部の処理が簡単な場合はそれでも良いのですが、ちょっと込み入った事をしようとすると一つ一つキャストすると見づらいので出来ればqsortでキャストをした方がスッキリするかなぁと思いまして。 int型のデータを扱うときはint*型にキャストをすれば良いと言うのは分かるのですが、char型のデータの場合はなぜchar**型となるのでしょう?char*型ではまずいですか?初歩的な質問ですいません。

  • noocyte
  • ベストアンサー率58% (171/291)
回答No.5

#4 です.補足します. > qsort( names, num,sizeof(names[0]), > (int(*compare)(const void *, const void *)) ); > ってやるとうちの環境ではやっぱりコンパイルエラーが出ちゃいます。 qsort の第4引数では,F1 型の関数として定義されている compare を F2 型 (= int(*)(const void*, const void*) 型) にキャストするわけですから, (F2)compare つまり (int(*)(const void*, const void*))compare としなければならないわけです.

  • t_nojiri
  • ベストアンサー率28% (595/2071)
回答No.3

http://www.linux.or.jp/JM/html/LDP_man-pages/man3/qsort.3.html まあ、UNIX等のマニュアルは引数後ろに普通に書かれてますけどね。

goo-ts
質問者

お礼

このサイトやリファレンスでは確かにこうなんですよね。でも qsort( names, num,sizeof(names[0]), (int(*compare)(const void *, const void *)) ); ってやるとうちの環境ではやっぱりコンパイルエラーが出ちゃいます。 引数を前にもってこないとダメなようです。

noname#22058
noname#22058
回答No.2

#No.1の者です。 プロトタイプ宣言には、そう書くことができます。 しかし、引数で、関数へのポインタを指定する際には そのようには書けません。

goo-ts
質問者

お礼

なるほど。 この記述については説明している本やサイトが少ないですね。 早々のご回答ありがとうございました。

noname#22058
noname#22058
回答No.1

qsortの第4引数は、 「const void *型の引数を2つ取り、戻り値がint型である関数へのポインタ」です。

goo-ts
質問者

お礼

早速のご回答ありがとうございます。 (int (*compare)(const void *, const void * )) 普通の関数のポインタ宣言のようにではダメなのでしょうか・・・

関連するQ&A

  • qsortについて

    qsort()で構造体をソートする時に、qsortの第3,4引数時の、「size t_t size, int (*cmp)(const void *a, const void *b)」の使い方が分かりません。 何故これを使ってソートが出来るのでしょうか?そもそも、構造体を送った時に、自作関数内での書き方にも疑問があります。 int main(void){ MAX dt[5]={・・・・}; //構造体変数宣言 ・・・・・ qsort(dt,5,sizeof(MAX),sum); //(*1) .... return 0; } int sum(const void *a, const void *b){ MAX *p1 = (MAX *)a; //(*2) MAX *p2 = (MAX *)b; //(*2) ・・・・ return ~; } ---------------------------------------------------- *1: 第3引数のsizeof(MAX)って何ですか? *2: 例えばint型が渡されてreturnする場合は、「return *(int *)a - *(int *)b 」でいいのに、   何故構造体の場合はこう書かないといけないのですか?

  • qsortの関数について

    qsortの関数について 下記のようにある本に関数の呼ぶ時、関数名(int_cmp)が引数(const void *, const void *)の後に書かれて理由を教えて頂きたい。よろしくお願いします。 質問のプログラム int int_cmp(const int *a ,const int *b) { ------ } int main { qsort(x, nx , y, (int (*)(const void *, const vois *))int_cmp);

  • qsortの関数ポイントについて

    qsortの関数ポイントについて int int_cmp(const int *a, const int *b) { ------ } main { qsort(x,nx,sizeof(int),(int (*) (const void *, const void * )) int_cmp); 上記の(1)関数のポイント,(int (*) (const void *, const void * )) int_cmpを  普通の(2)関数ポイント(* int_cmp) (const void *, const void * )で呼べない  理由 、また、(1)(2)は、同じ意味でしょうか、教えて頂きたい。

  • C言語のqsortについて

    現在、qsortのコードに取り組んでいます。 if (strcmp(ad, "ASC") == 0) { qsort(sin, sizeof(cnt), sizeof(sin[0]), cmp_u); } else { qsort(sin, sizeof(cnt), sizeof(sin[0]), cmp_d); } 恐らくこちらのqsortでの第二引数が書き方を間違えていると思うのですが、修正の方法が分からず、どなたか教えて頂けないでしょうか? #include <stdio.h> #include <time.h> #include <string.h> #include <stdlib.h> static char ad[10]; int cmp_u(const void* a, const void* d) { return strcmp((char*)a, (char*)d); } int cmp_d(const void* a, const void* d) { return strcmp((char*)d, (char*)a); } int main() { int num1, num2; char op; float answer; int r,i; FILE* fp; char c[11]; char sin[1000][1000]; char ad[8]; fp = fopen("log.txt", "a+"); if (fp == NULL) { printf("ファイルオープン失敗\n"); return -1; } while (1) { r = scanf("%d%c%d", &num1, &op, &num2); if (r != 3) { puts("input error"); return 1; } if (op == '+') { answer = num1 + num2; } else if (op == '-') { answer = num1 - num2; } else if (op == '*') { answer = num1 * num2; } else if (op == '/') { answer = (float)num1 / num2; } time_t t = time(NULL); struct tm* tm = localtime(&t); printf("%d/%02d/%02d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); printf("%02d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec); printf("%d%c%d,%f\n", num1, op, num2, answer); fprintf(fp, "%d/%02d/%02d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); fprintf(fp, "%02d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec); fprintf(fp, "%d%c%d,%f\n", num1, op, num2, answer); printf("計算を続けますか?"); scanf("%s\n", &c); if (strcmp(c, "no") == 0) { break ; } } fclose(fp); fp = fopen("log.txt", "r"); int cnt = 0; for (i = 0;i < 1000;i = i + 1) { if (fgets(sin[i], sizeof(sin[0]), fp)) ++cnt; else break; } fclose(fp); printf("ASC or DESC: "); scanf("%s", ad); if (strcmp(ad, "ASC") == 0) { qsort(sin, sizeof(cnt), sizeof(sin[0]), cmp_u); } else { qsort(sin, sizeof(cnt), sizeof(sin[0]), cmp_d); } for (i = 0;i < cnt;i = i + 1) { printf("%s", sin[i]); } return 0; }

  • bsearch関数

    エラー 関数 `typespec' 内: 警告: 互換性のないポインタ型からの引数 5 個の `bsearch' を渡しますです int typespec(void) { static char *types[] = { "char", "int", "void" }; char *pt = token; if (bsearch(&pt, types, sizeof(types)/sizeof(char *), sizeof(char *), compare) == NULL) return NO; else return YES; } int compare(char **s, char **t) { return strcmp(*s, *t); } サイトや教科書を見たり読んだりして、標準ヘッダとも被らないように試みてみたのですが、余計エラーが増えてしまいます。「引数 5 個」ということは、全部アウトなんでしょうか? 過去の質問を見てみてもピンときません。暇な方がいるなら是非初心者にもわかりやすいアドバイスください。 (OSはLINUX。端末はGNOMEです)

  • C言語のqsortについて

    下記の課題に取り組んでおり、qsortをファイルの行数分のみ出力するプログラムを書いているのですが if (strcmp(ad, "ASC") == 0) { qsort(sin,1000 , sizeof(sin[0]), cmp_u); if (sin[i][0] == 0) { break; } } else { qsort(sin, 1000, sizeof(sin[0]), cmp_d); if (sin[i][0] == 0) { break; } この書き方ではエラーが出てしまい、どなたか書き方を教えて頂けないでしょうか? 【課題】 電卓アプリケーションの作成  以下の機能を満たすアプリケーションを作成してください。  ※画面の表示やログの形式は原則として例示されている内容に従ってください。  1) 四則演算が出来ること  2) コンソールから計算対象となる数値と演算子を受け取る    例) 5 + 6  3) 結果をコンソール上に表示する    例) 11  4) 演算結果と実行した日、時間、秒をログファイルとして保存する    ログの上限は1000行とする(上限を超過するケースはひとまず考慮しなくて良い)    例) log.txt に 以下の内容を記録      2015/04/27 14:30:51, 5 + 6, 11  5) コンソールからパラメータを受け取り、ログを実行時間の昇順/降順に並べ替えて    コンソールに表示する    例) ASC を入力      2015/04/26 10:20:00, 5 + 6, 11      2015/04/27 14:30:51, 7 - 6, 1      2015/04/27 15:30:00, 7 + 8, 15      2015/04/28 14:30:51, 8 + 9, 17    例) DESC を入力      2015/04/28 14:30:51, 8 + 9, 17      2015/04/27 15:30:00, 7 + 8, 15      2015/04/27 14:30:51, 7 - 6, 1      2015/04/26 10:20:00, 5 + 6, 11 【現在のソースコード】 #include <stdio.h> #include <time.h> #include <string.h> #include <stdlib.h> static char ad[10]; int cmp_u(const void* a, const void* d) { return strcmp((char*)a, (char*)d); } int cmp_d(const void* a, const void* d) { return strcmp((char*)d, (char*)a); } int main() { int num1, num2; char op; float answer; int r,i,n; FILE* fp; char c[11]; char sin[1000][1000]; char ad[8]; fp = fopen("log.txt", "a+"); if (fp == NULL) { printf("ファイルオープン失敗\n"); return -1; } while (1) { r = scanf("%d%c%d", &num1, &op, &num2); if (r != 3) { puts("input error"); return 1; } if (op == '+') { answer = num1 + num2; } else if (op == '-') { answer = num1 - num2; } else if (op == '*') { answer = num1 * num2; } else if (op == '/') { answer = (float)num1 / num2; } time_t t = time(NULL); struct tm* tm = localtime(&t); printf("%d/%02d/%02d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); printf("%02d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec); printf("%d%c%d,%f\n", num1, op, num2, answer); fprintf(fp, "%d/%02d/%02d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); fprintf(fp, "%02d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec); fprintf(fp, "%d%c%d,%f\n", num1, op, num2, answer); printf("計算を続けますか?"); scanf("%s\n", &c); if (strcmp(c, "no") == 0) { break ; } } fclose(fp); fp = fopen("log.txt", "r"); for (i = 0;i < 1000;i=i+1) { fgets(sin[i], sizeof(sin[0]), fp); //if (strcmp(sin, "NULL") == 0) { if (sin[i][0] == 0) { break; } } fclose(fp); printf("ASC or DESC: "); scanf(" %s", ad); if (strcmp(ad, "ASC") == 0) { qsort(sin,1000 , sizeof(sin[0]), cmp_u); if (sin[i][0] == 0) { break; } } else { qsort(sin, 1000, sizeof(sin[0]), cmp_d); if (sin[i][0] == 0) { break; } } for (i = 0;i < 1000;i = i + 1) { if (sin[i][0] == 0) { break; } printf("%s", sin[i]); } return 0; }

  • 関数の引数をvoid*でキャストする

    最近見かけたCのプログラムで、関数の引数の型は void* なのですが、その関数を使うときに 引数をvoid*でキャストしていました。 例えば、 func ( (void*) p ); こういうことです。 私の知っている知識では、 void* と 任意の型のポインタは キャストなしに相互に代入可能です。 関数の引数でも、キャストは要らないものだと思っていました。 そうすると、引数を void* でキャストするのは無意味だと思うのですが、・・・ 違うのでしょうか。処理系によるとか。 逆に、関数の引数の型がchar*などで、渡すものが void* のときはどうなのでしょうか。 下のプログラムは、関数byte_orderの引数の型はvoid*ですが、int型へのポインタ( &a )を設定しています。私の環境では、コンパイルエラーも警告もないし、動作も正常です。 #include <stdio.h> #include <string.h> void byte_order(void *vp) { char char_array[4]; strncpy(char_array, vp, 4); printf("出力します:%x %x %x %x\n", char_array[0], char_array[1], char_array[2], char_array[3]); } int main(void) { int a = 0x12345678; byte_order(&a); return 0; } このプログラムは単なる一例であって、質問はバイトオーダに関するものではありません。 また、C言語の質問であって、C++ではありません。

  • こんなコンパイルエラーがでます。

    OS:WinNT4.0 環境:VC++6.0 MFC コンパイル時に次のエラーが出て来ました。 error C2664: 'qsort' : 4 番目の引数を 'int (const void *,const void *)' から 'int (__cdecl *)(const void *,const void *)' に変換できません。 (新しい機能 ; ヘルプを参照)スコープ内でこの名前を持つ関数でターゲット型に一致するものはありません。 qsort関数の引数関数を'int xxxxxxx(const void *,const void *)に宣言したところこのエラーが出てきました。 何がなんだかさっぱりわかりません。 ぜひお願いします。

  • C言語の引数について

    「戻り値、値渡し、ポインタ渡しを用いたサブ関数を実装する」 という課題に対して、calc_proc関数を実装したのですが、下記のコード提出後に 「引数で値をmainに戻すパターンを実装する」 との要望を受けたのですが、やり方が分からず、どなたか修正方法、追加方法を教えて頂けないでしょうか? 「ソースコード」 #include <stdio.h> #include <time.h> #include <string.h> #include <stdlib.h> #define CALC (3) #define FROM_YEAR (1900) #define MAX_LINE (1000) #define MAX_ROW (1000) float calc_proc(int* n1, char op, int n2) { switch (op) { case '+': (float)*n1 + n2; break; case '-': (float)*n1 - n2; break; case '*': (float)*n1 * n2; break; case '/': if (n2 == 0) { puts("input error"); return 1; } (float)(float)*n1 / n2; break; default: printf("input error"); } return 0; } int cmp_u(const void* a, const void* d) { return strcmp((char*)a, (char*)d); } int cmp_d(const void* a, const void* d) { return strcmp((char*)d, (char*)a); } int main() { int num1, num2; char op; float answer; int r,i; FILE* fp; char e[11]; char sin[MAX_LINE][MAX_ROW]; char ad[8]; fp = fopen("log.txt", "a+"); if (fp == NULL) { printf("ファイルオープン失敗\n"); return -1; } while (1) { r = scanf("%d%c%d", &num1,&op, &num2); if (r != CALC) { puts("input error"); return 1; } answer = calc_proc(&num1, op, num2); time_t t = time(NULL); struct tm* tm = localtime(&t); printf("%d/%02d/%02d ", tm->tm_year + FROM_YEAR, tm->tm_mon + 1, tm->tm_mday); printf("%02d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec); printf("%d%c%d,%f\n", num1, op, num2, answer); fprintf(fp, "%d/%02d/%02d ", tm->tm_year + FROM_YEAR, tm->tm_mon + 1, tm->tm_mday); fprintf(fp, "%02d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec); fprintf(fp, "%d%c%d,%f\n", num1, op, num2, answer); printf("計算を続けますか?"); scanf("%s", e); if (strcmp(e, "no") == 0) { break ; } } fclose(fp); fp = fopen("log.txt", "r"); int cnt = 0; for (i = 0;i < MAX_LINE;i = i + 1) { if (fgets(sin[i], sizeof(sin[0]), fp)) ++cnt; else break; } fclose(fp); printf("ASC or DESC: "); scanf("%s", ad); if (strcmp(ad, "ASC") == 0) { qsort(sin, cnt, sizeof(sin[0]), cmp_u); } else { qsort(sin, cnt, sizeof(sin[0]), cmp_d); } for (i = 0;i < cnt;i = i + 1) { printf("%s", sin[i]); } return 0; }

  • なんでchar型なんでしょうか??

    なぜここ(☆☆)で char型なのかが よく・・いやさっぱりわかりません。 他の型ではだめなんでしょーか? この関数自体は挿入ソートだとわ思われるんで なんとなくこの☆☆(から以下三行あたりまで) のところの動作の意味はわかるんですが なぜchar型でなくてはならないのかが。 知っている方いたら どうか教えてください。 typedef User* PUser; typedef int (*CFT)(const void*, const void*); void ssort(void* base, unsigned int n,        unsigned int sz, CFT cmp) {    for (int i = 0; i < n - 1; i++) for (int j = n - 1; i < j; j--) {          char* pj = (char*)base + j * sz; //←ここ☆☆          char* pj1 = pj - sz;  //とここ☆☆           if (cmp(pj, pj1) < 0) { for (int k = 0; k < sz; k++) { char temp = pj[k]; pj[k] = pj1[k]; pj1[k] = temp; }     }    } /* ssort関数の引数の関数ポインタで 利用される比較関数 */ int cmp1(const void* p, const void* q) { return strcmp(PUser(p)->name, PUser(q)->name); } int cmp2(const void* p, const void* q) { return PUser(p)->dept - PUser(q)->dept; }

専門家に質問してみよう