ポインタまわりはC言語の良い所でもあり,悪い所でもあると思います.
ポインタのキャストについての理解が明暗を分けると思いますので,
いくつか検証用のコードを実行していただきたいと思います.
C言語では,型の違うもののアドレスもポインタ変数に格納することができます.
そのことは,次のコードでわかります.
c
1#include <stdio.h>
2int main(int argc, char *argv[])
3{
4 printf("%lu %lu %lu %lu\n", sizeof (char), sizeof(char *), sizeof(char **), sizeof(char ***));
5 printf("sizeof(unsigned int):%lu sizeof(unsigned long):%lu\n", sizeof(unsigned int), sizeof(unsigned long));
6 return 0;
7}
char
型は1バイトですが,char*
は8バイト,char**
も8バイト,char***
も8バイト…です.
そしてvoid*
も8バイトです.(void
は1バイトですが,void
型の変数は宣言できません)
バイト数が一緒という点では,char*
型のポインタ変数に,char***
型の値も格納できるということです.
さらに,私の環境ではunsigned long
も8バイトであるため,ポインタ変数を格納するために十分です.
ポインタ変数に関連したキャストが適当でもプログラムが動いてしまうのは,
ポインタ変数とは8バイトの値を格納する変数であるからです.
次は実際にchar*
とint*
を混ぜます.
c
1#include <stdio.h>
2int main(int argc, char *argv[])
3{
4 int i;
5 int data[] = {0x00112233, 0x44556677};
6 int *iptr = data;
7 char *cptr = data;
8 printf("iptr = ");
9 for (i = 0; i < 2; i++)
10 printf("%08x ", iptr[i]);
11 printf("\ncptr = ");
12 for (i = 0; i < 8; i++)
13 printf("%02x ", cptr[i]);
14
15 printf("\niptr([]) = ");
16 for (i = 0; i < 2; i++)
17 printf("%lx ", &iptr[i]);
18 printf("\niptr(+) = ");
19 for (i = 0; i < 2; i++)
20 printf("%lx ", iptr + i);
21 printf("\ncptr = ");
22 for (i = 0; i < 8; i++)
23 printf("%lx ", &cptr[i]);
24 return 0;
25}
本来int型のデータを,無理矢理char
型のポインタで参照し,中身を表示させています.
Intel x86であればリトルエンディアンなので,
データ部分は「33 22 11 00 77 66 55 44」と表示されると思います.
char*
とint*
の違いは,添字を使ったり加減算をした時に出てきます.
試していただければ,&iptr[i]とiptr+iは同じであることがわかると思います.
そして,&iptr[0]と&iptr[1]は4バイト離れていて,&cptr[0]と&cptr[1]は1バイト離れています.
ポインタ変数の型とは,このように添字を使ったり加減算をした時に,
アドレスを何バイト分ずらすかという所に違いがあります.
まとめると,アドレスとは,64ビット環境では64ビットの値でしかありません.
そのため,ポインタ変数同士の代入は型が違っていてもできます.
またポインタ変数に,ポインタ変数へのアドレスを代入することもできます.
示してくださった例では,qsort関数の仕様上,
比較対象の要素2つへのポインタaとbをもらうことになっています.
しかし比較しなければならないのはポインタ変数aとbの中身(文字列の先頭アドレスを格納する配列の要素のアドレス)ではなく,
文字列の先頭のアドレスをstrcmp()に渡して得られる文字同士のはずです.
例えばこのような例であれば,
c
1char *data[3];
2data[0] = "a";
3data[1] = "bb";
4data[2] = "ccc";
compareStringFor_qsort()が受け取ることができるのは,data[0],data[1],data[2]ではなく,
&data[0],&data[1],&data[2]なのです.
しかもchar**
型のはずが,void*
型にキャストされてしまっているんです.
そこでまずは,(char **)a
でchar**
型にキャストして正しい型(&data[0]と同じ型)にして,
*(char **)a
で文字列の先頭アドレスを得ています(data[0]と同じ型).
混乱の原因はchar **
がqsortライブラリの中でキャストされてvoid *
になってしまう所だと思います.
int配列などのソートならばこれで問題ないのですが,今回は文字列の配列のソートということで少しやっかいです.
C言語は良くも悪くも,ポインタ変数のキャストルールがゆるゆるです.
明示的にキャストすれば何でもOKです.
わかりにくいところがありましたら,コメントください.
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2015/12/25 20:57