最近仕事でC言語を使うことがある。主に古いソースの改造とかバグつぶしなのだが、既存の仕組みを使いつつ、後から増設ということをC言語でやったりしている。そのときによくポインタのポインタをもっていて、その値を参照したり書き換えたりすることがある。つまりデータの格納領域は元のままで、それにアクセスする部分を後から付け足すって感じのことをやるためである。
何をいいたいかというと単純に、ポインタを使いまくっているということである。
そして、昨日見事に罠にはまる(ぉ、どこぞの狩人が仕掛けた罠かはよくわからないが・・・、やはり言語使用をよく読んでいなかったためというのがでかいが、とりあえず罠であった。
で、具体的にわなの内容というのは、簡単にいえばchar *とchar []の違いである。今まではほぼ同じ物であると思っていた。同じ文字列定数を参照している場合とか以外では、使う場合にはほとんど差が無いのは事実である。しかしこの変数のポインタを見たときには値がまったく違っていたのであった・・・
| @例 |
char name1[40];//文字列がはいってる
char name2[40];//文字列がはいってる
typedef struct tagText
{
char **item1;
char **item2;
} TText;
TText items[] =
{
{&name1,&name2},
};
int main(int argc , char **argv)
{
printf("%s\n",*(items[0].item1));
printf("%s\n",*(items[0].item2));
return 0;
}
|
最初に書いたソースはこんな感じである。何がしたいかというとテーブルに文字列のポインタを持ちたいのである、何故こんなことになるかというと、すごい数のグローバル変数が合って、それぞれに対して同じ処理をするルーチンがその数だけ並んでいる関数とかが、そこら中にあったりしていて、それをまとめて処理したいため、変数へのテーブルを作るという話である。
それで、とりあえず作って見た、このソースをコンパイルしてみると妙な警告が出て、実行するとセグメント例外で落ちる(ぷ。正直最初何が悪いのか理解が出来なかった。そこでとりあえず、実験的に色々弄くってみることにした。
その結果*(item[0].item1)がとんでもない値をさしていることがわかり、この部分で例外を吐いているらしいということがわかる。しかし最初は理由がさっぱりであり、一瞬コンパイラを疑いそうになった(ぉ。しかしたいていこういう場合はこちらが悪いので、思い直して色々実験してみると、どうも&name1と&name2のアドレスが、どうも期待どうりの値を返していないらしいということがわかる。
それはどういうことかというと、&name1の値とname1の値がなんと同じ値を返すのである。つまりchar
*の場合とchar [n]の場合では&演算子の返す値が異なっているのである!!、実験例を以下に示すと
| @実験例 |
{
char *a = "asdf";
char b[5] = "1234;;
printf("a=%x\n",a); /* aの値を表示 */
printf("&a=%x\n",&a);/* &aの値の表示 */
printf("b=%x\n",b); /* bの値の表示 */
printf("&b=%x\n",&b);/* &bの値の表示 */
}
|
これを実行するとaの場合は違う値が表示されbの場合は同じ値が表示される。では何でこうなるかと考えると、char[n]はラベルのようなものであると考えられる、char
*の場合はメモリにそのアドレスを指す領域が確保されるが、char[n]の場合はコンパイル時ないしリンク時ないし実行開始時にその値が確定されるいわば定数値に近いものであると理解することができる。
じゃーどうしろと?という話になるが、この場合は簡単である。もともとchar [n]は配列へのポインタであるため。構造体の定義部分をchar
**からchar *にして直接その配列をさしてしまえばよい。(じゃあ最初からそうしろ!!)という意見が多数を占めるはずだが、最初に考えた時にアドレスを指している変数のアドレスを持っていれば、確実であるという発想にとらわれたためこうなったというのは否定できない(ぉ
まあ、実際配列の再確保などが起こる場合に関してはその手法が使えないのは事実である。しかし思うのだがchar
[n]とchar[]は意味が違うということか?、でないと
char a[] = new char[10];
とかいった書き方をした場合に、aと&aの値は同じであるということになってしまい、そのアドレス値は何処に格納されているのか?という話題になってしまい、a[]までラベルだとしたら動的な配列の再確保とかが出来ないということになってしまう。じゃあ最初から
char a[] = "asdf1234";
とかやった場合はどうなのだろう・・この場合もaと&aの値は同じなのだろうか?
この辺も実験しておく必要があると思われるな・・・
また気が向いた時にでも(ぉ、
しかし、こんな事ばっかやってると迷子の迷子のポインタ君になりそうだ