今更ですが qsort() を使って char *array[] = { "foo", "baa", ...
な配列をソートしてみました。qsort() に渡す比較関数の型は
int (*__compar_fn_t) (const void *, const void *); なので
C
1int cmpword(const void *word1, const void *word2) { 2 return strcmp(*(char**)word1, *(char**)word2); // (A) 3}
という関数を qsort() に渡すことで問題なく動作しました。
ここの「(char**)
」というキャストは「word1 というポインタは
const void * 型として渡されるけど、実は char へのポインタを指している。即ち、word1はダブルポインタである」といった意味になるでしょう。
ところで、 char *array[]; はポインタの配列であり、word1 はその中の一要素を指すので、たいていはその前後にもポインタが並んでいます。なので、「実は word1 はポインタの配列を指している」というキャストもあるのでは?と考え、しばし試行錯誤したところ、
C
1int cmpword(const void * word1, const void * word2) { 2 return strcmp(*(char(*)[])word1, *(char(*)[])word2); // (B) 3}
が警告なくコンパイルが通り実行できることがわかりました。
なんとなくそれっぽい(笑)と思ったのですが、残念ながら実行結果がおかしいことに気づいて、調べてみたら違う事がわかりました。
次は (char**) と (char(*)[])
が違う事を確認した最終的なコードです。
C
1#include <stdio.h> 2#include <string.h> 3 4// 引数の値を表示する疑似 strcmp() 関数 5int pseudo_strcmp(const char *s1, const char *s2) { 6 printf("(%p, %p)", s1, s2); 7 return strcmp(s1, s2); 8} 9int cmpword1(const void *word1, const void *word2) { 10 printf("cmpword1() call strcmp"); 11 return pseudo_strcmp(*(char**)word1, *(char**)word2); // (A) 正しい 12} 13int cmpword2(const void *word1, const void *word2) { 14 printf("cmpword2() call strcmp"); 15 return pseudo_strcmp(*(char(*)[])word1, *(char(*)[])word2); // (B) ? 16} 17 18int main(void) 19{ 20 char *arr[] = { "the", "main", "story", "arc", "concerns" }; 21 22 printf("まず arr[] の配置を確認\n"); 23 for (int i = 0; i < 4; i++) 24 printf("[%d] %p: %p -> \"%s\"\n", i, &arr[i], arr[i], arr[i]); 25 printf("\n"); 26 27 printf("cmpword1() と cmpword2() は違う?\n"); 28 printf(" => %d\n", cmpword1(&arr[2], &arr[3])); 29 printf(" => %d\n", cmpword2(&arr[2], &arr[3])); 30 return 0; 31}
二つのキャストが同等なら (A) も (B) も strcmp() に同じ値を引数として渡すはずです。それを確認するため、疑似strcmp()関数の中で引数の値を表示させました。実行結果は一目瞭然。
$ ./a.out まず arr[] の配置を確認 [0] 0x7ffc87881250: 0x562b8bd6ea3f -> "the" [1] 0x7ffc87881258: 0x562b8bd6ea43 -> "main" [2] 0x7ffc87881260: 0x562b8bd6ea48 -> "story" [3] 0x7ffc87881268: 0x562b8bd6ea4e -> "arc" cmpword1() と cmpword2() は違う? cmpword1() call strcmp(0x562b8bd6ea48, 0x562b8bd6ea4e) => 18 cmpword2() call strcmp(0x7ffc87881260, 0x7ffc87881268) => -6
(B) が strcmp() に渡したのは cmpword2() が受け取った引数 word1 の値そのものでした。
しかし「*(char(*)[])word1,
」という引数は、キャストを外してしまえば「*word1,
」ですから、ポインタ word1 を使った間接参照の格好なのに、word1 の値がそのまま strcmp() に渡る・・・これは奇妙に思えます。
(char(*)[])
というキャストはどういう意味でしょうか。なぜ奇妙なことになるのでしょうか。
以上は gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 を使いました。
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/10/08 07:33 編集
2020/10/08 08:32 編集
2020/10/08 08:44
2020/10/08 11:12
2020/10/08 11:21
2020/10/08 11:22