構造体のアドレスと構造体の最初のメンバーのアドレスは同一であるとして良いか?
前に見かけたコードなんですが、
例えば(コンセプトが同じと思って下さい)
struct hoge {
char name[32];
//以下その他のメンバーが続く
} foo[16];
のような配列があって、これをqsort
を使ってname
でソートする時
比較関数で、
int cmp(const void *a, const void *b){
const struct hoge *x = a;
const struct hoge *y = b;
return strcmp(x->name, y->name);
}
のようなことをするわけですが、
構造体の先頭メンバーのアドレスが構造体の先頭アドレスと同じである(違う理由もないと思いますが)として、
int cmp(const void *a, const void *b){
return strcmp((char *)a, (char*)b);
}
のように書けるように思います。
(1)これって問題ありですか無しですか?
- ポインタの型としては
struct hoge*
とchar *
で異なるから
このような使い方をした場合動作未定義(?) - 先頭のアドレスが同じであって、メモリ配置上実質的に同じであるから問題無し。
別の例として、
struct foo {
char data[7][7];
} f ;
のような2次元配列をラップしている構造体f
がある。
今作業用の配列
char data[7][7];
があって、何らかの作業を行ったあとf
に格納したい。
この時、
f = *(struct foo*)data;
のようにしたら
(2)これって動作未定義?
以下は私の考え
(2-1)memcpy(&f, data, sizeof(data));
のように動作する
先頭のアドレスが同じであるので、実質memcpy(f.data, data, sizeof(data));
であるので問題無い。
(2-2)memcpy(&f, data, sizeof(f));
のように動作する。
構造体には一般にパディングが存在し、この場合も
sizeof(f) > sizeof(data)
でありdata
の範囲外の読み出しをするから動作未定義である。(2-2)の
memcpy
とは違って、構造体の代入の場合パディングはコピーされない(?)のでsizeof(f) > sizeof(data)
であったとしても問題無い。
(私の予想としてはおそらくパディングもコピーされる)最後のメンバーにはパディングが存在しない(?)。よって
sizeof(f) == sizeof(data)
であって、メモリイメージ上同じであるから問題無い。
(2-3)一般にchar
のメモリ上の配置にはアライメント制約が存在しない。(私的予想では先頭部分はアライメントを考慮した配置がされている)なので、char
より大きいサイズのデータの読み出し(つまり*(struct foo*)data
すること)は、アライメント制約ルール違反であり動作未定義である。(構造体としてラップされてはいるが実質同じcharの2次元配列なので関係無い?またはmemcpy
のように1バイトずつコピーがされると考えて良いのであれば関係無い?)