はじめに
タイトルにもある通り、C言語でアドレスのアドレスを参照していると思ったけど問題なかった話です。
C言語の配列の表記方法の曖昧さに混乱させられたのでメモしておきます。
本題
問題の箇所
bzero((char *)&buf, sizeof(buf));
bufはchar型の配列です。
「buf」だけで配列の先頭アドレスを参照しているのに「&buf」としているためアドレスのアドレスを参照している!?
本来ならこう書くべきです。
bzero((char *)buf, sizeof(buf));
あるいは
bzero((char *)&buf[0], sizeof(buf));
しかし、一発でメモリを破壊してしまいそうなコードなのにプログラムは期待通りに動いている。。。
色々調べてみましたが、結論から言うとC言語の文法的に問題ないそうです。
(C言語の配列アドレス表記法として認められている?)
参考 POINTER
引用↓
【配列→ポインタの読み換え】
式の中では、配列は「先頭要素へのポインタ」に 読み換えられる。
int a[10];
の時、式の中では、a と &a[0]は同じ意味となる。ただし、以下の個所は例外である。
1.sizeof演算子のオペランド
sizeof演算子は、ポインタのサイズではなく、配列自体のサイズを返す。2.& 演算子のオペランド
& 演算子は、配列全体のアドレスを返す。
配列から読み換えられたポインタは左辺値を持たないため、
& 演算子のオペランドにはならないはずであるが、この例外規則のため、
& でアドレス(配列の先頭要素のアドレスではなく、配列全体のアドレス)が
取得できる。この規則は初心者を混乱させることがある
(例えば scanf("%s", buf) でなく、scanf("%s", &buf)と書いても
正常に動いてしまう(正常に動いたように見えてしまう)が、
メリットは今ひとつわからない。3.初期化時の文字列リテラル
char の「配列」を初期化する場合の文字列リテラルは、
中括弧内に文字を区切って書く初期化子の省略形である。
char の「ポインタ」を初期化する場合の文字列リテラルとの
違いに注意すること。
例外規則とかやめてほしい。。。
つまり、bufや&buf[0]は配列の先頭アドレス、&bufは配列全体のアドレスで、両者は異なるアドレスだけど同じアドレスであるかのように動いてしまう、ということらしい。
おわりに
配列・ポインタを扱うところで変な書き方をしても動いてしまうのがC言語の怖いところですが、表記法に曖昧な部分があることがC言語をより複雑にしているというのを身をもって知りました。