31347


前に見かけたコードなんですが、  
例えば（コンセプトが同じと思って下さい）

[code]

    struct hoge {
        char name[32];
        //以下その他のメンバーが続く
    } foo[16];
    
[/code]

のような配列があって、これを`qsort`を使って`name`でソートする時  
比較関数で、

[code]

    int cmp(const void *a, const void *b){
        const struct hoge *x = a;
        const struct hoge *y = b;
        return strcmp(x->name, y->name);
    }
    
[/code]

のようなことをするわけですが、  
構造体の先頭メンバーのアドレスが構造体の先頭アドレスと同じである（違う理由もないと思いますが）として、

[code]

    int cmp(const void *a, const void *b){
        return strcmp((char *)a, (char*)b);
    }
    
[/code]

のように書けるように思います。  
（１）これって問題ありですか無しですか？

  * ポインタの型としては`struct hoge*` と`char *`で異なるから  
このような使い方をした場合動作未定義（？）

  * 先頭のアドレスが同じであって、メモリ配置上実質的に同じであるから問題無し。

* * *

別の例として、

[code]

    struct foo {
        char data[7][7];
    } f ;
    
[/code]

のような２次元配列をラップしている構造体`f`がある。  
今作業用の配列

[code]

    char data[7][7];
    
[/code]

があって、何らかの作業を行ったあと`f`に格納したい。  
この時、

[code]

    f = *(struct foo*)data;
    
[/code]

のようにしたら

（２）これって動作未定義？

以下は私の考え  
（２－１）`memcpy(&f, data, sizeof(data));`のように動作する

先頭のアドレスが同じであるので、実質`memcpy(f.data, data, sizeof(data));`であるので問題無い。

（２－２）`memcpy(&f, data, sizeof(f));`のように動作する。

  1. 構造体には一般にパディングが存在し、この場合も`sizeof(f) > sizeof(data)`であり`data`の範囲外の読み出しをするから動作未定義である。

  2. （２－２）の`memcpy`とは違って、構造体の代入の場合パディングはコピーされない（？）ので`sizeof(f) > sizeof(data)`であったとしても問題無い。  
（私の予想としてはおそらくパディングもコピーされる）

  3. 最後のメンバーにはパディングが存在しない（？）。よって`sizeof(f) == sizeof(data)`であって、メモリイメージ上同じであるから問題無い。

（２－３）一般に`char`のメモリ上の配置にはアライメント制約が存在しない。（私的予想では先頭部分はアライメントを考慮した配置がされている）なので、`char`より大きいサイズのデータの読み出し(つまり`*(struct
foo*)data`すること）は、アライメント制約ルール違反であり動作未定義である。(構造体としてラップされてはいるが実質同じcharの２次元配列なので関係無い？または`memcpy`のように１バイトずつコピーがされると考えて良いのであれば関係無い？)


CC BY-SA 3.0
2016-12-23T18:42:09.467
31346
2016-12-27T02:53:37.447
2016-12-23T21:27:50.837
5044
5044
question
c
構造体のアドレスと構造体の最初のメンバーのアドレスは同一であるとして良いか？


最初の質問 (構造体の先頭メンバーのアドレスが構造体の先頭アドレスと同じであるか) については、「同じ」が答えです。

C 言語仕様のドラフト N1570 を言うのを見つけたので、ざっと見たところ 6.7.2.1 Structure and union specifiers
のところに

> Within a structure object, the non-bit-field members and the units in which
> bit-fields reside have addresses that increase in the order in which they
> are declared. A pointer to a structure object, suitably converted, points to
> its initial member (or if that member is a bit-field, then to the unit in
> which it resides), and vice versa. There may be unnamed padding within a
> structure object, but not at its beginning.

とありました。ざっと訳すと

>
> 構造体のビットフィールド以外のメンバーと、ビットフィールドがまとめられたユニットは、宣言された順番に大きくなるようなアドレスを持つ。構造体へのポインタは、ちゃんと変換すれば、最初のメンバーを指す。逆もまた同じ。構造体はパディングされることがあるが、先頭にはない。

ということなので、メンバーは宣言された順番に並んでいるし、最初のメンバーは構造体の先頭です。

* * *

(以下、追記)

質問の後半部分についてですが、言語仕様で関係のありそうなところを取り出してみると、6.2.6 Representation of types の
6.2.6.1 General に

> When a value is stored in an object of structure or union type, including in
> a member object, the bytes of the object representation that correspond to
> any padding bytes take unspecified values.51)

とあり、脚注 51 には

> 51) Thus, for example, structure assignment need not copy any padding bits.

とあります。つまり、パディングの値は不定で、したがって代入の時にパディングをコピーする必要はない、と言っています。逆に言えば、コピーしても構わないということでしょう。

また、6.7.2.1 Structure and union specifiers のところに

> There may be unnamed padding at the end of a structure or union.

つまり、構造体の最後にもパディングが来てもよいと言っています。  
まとめると、構造体の最後にパディングが来てもよいし、そのパディングを代入の際にコピーしてもよいということになります。

この上で `f = *(struct foo*)data;` を見ると、キャストしているので構造体の代入ですから、厳密にいえば未定義だと思います。とはいえ、

[code]

    struct foo {
        char data[7][7];
    } f ;
    
[/code]

では、アラインメントの必要なメンバーがないので、現実的には、最後にパディングを入れることは、まずないと思います。


CC BY-SA 3.0
2016-12-23T20:35:15.807
31347
2016-12-27T02:53:37.447
2016-12-27T02:53:37.447
3605
3605
31346
answer


まとめのようなもの（なのでコミュニティｗｉｋｉで）

（１）については、Hidekiさんの回答の通りであり問題無い。

（２）

（１）から  
`memcpy(&f, data, sizeof(data));`と`memcpy(f.data, data, sizeof(data));`は同じである。  
（ごちゃごちゃ考えるよりこうするのが問題無いやり方ｗ）

（２－２－１）  
検証コード

[code]

    #include <stdio.h>
    
    struct foo {
        char data[7][7];
    } f;
    
    int main (void){
        printf("size of f : %zu\n", sizeof f);
    }
    
[/code]

[結果](http://ideone.com/Lanl2b)：

[code]

    size of f : 49
    
[/code]

この結果からはこの場合パディングは存在しない。（実装依存の可能性もあるかも）

（２－２－２）  
検証コード

[code]

    #include <stdio.h>
    #include <string.h>
    
    struct foo2 {
        char data[7][7];
        int no;
    } f2;
    
    int main (void){
        printf("size of f2 : %zu\nsize of int : %zu\n", sizeof f2, sizeof(int));
        struct foo2 a, b;
        memset(&a, 0, sizeof(a));
        memset(&b, -1, sizeof(b));
        b.no = 0x11111111;
        a = b;
    
        unsigned char *p = (void *)&a;
        for(size_t i = 0; i < sizeof a; ++i){
            printf("%02x ", *p++);
            if((i + 1) % 7 == 0)puts("");
        }
        puts("");
    
    }
    
[/code]

[結果](http://ideone.com/cwamCF)

[code]

    size of f2 : 56
    size of int : 4
    ff ff ff ff ff ff ff 
    ff ff ff ff ff ff ff 
    ff ff ff ff ff ff ff 
    ff ff ff ff ff ff ff 
    ff ff ff ff ff ff ff 
    ff ff ff ff ff ff ff 
    ff ff ff ff ff ff ff 
    ff ff ff 11 11 11 11 
    
[/code]

この結果からは、  
構造体の代入においてメンバー相互に代入が行われるのみではなくパディングもコピーされる。  
おそらくメンバー個別に代入が行われるのではなく全体が`memcpy`のようにコピーされることが推測される。（実装依存？）

（２－２－３）  
（２－２－１）と（２－２－２）の結果から最後のメンバーの後ろには(構造体自体における)パディングはないと予想される。  
なので、  
メンバーのタイプが`char`の配列の場合でパディングが存在しない場合は問題無し。  
これが予想される結論

（２－３）  
８６系のマシンではアライメントバイオレーション例外を起こさないので検証コードが書けないｗ。  
しかし、例外を起こすマシンでは、

[code]

    char a[5];
    int d = *(int*)&a[1];
    
[/code]

のようなコードは例外を起こすに違いない。

(`char`の場合は問題無いだろうから拡張した問題として）  
問題は、

[code]

    struct Integer {
        int d;
    };
    
[/code]

のような構造体でラップした時に  
`int`で読み出されるのか(OUT!)  
`memcpy`のようにバイトで読み出されるのかということ。(OK!)  
今のところ「`memcpy`のようにバイトで読み出される」というのが予想だが、  
（構造体自体のサイズは一般に不定であり中身が`int`（特定のプリミティブ）であるからといって`int`で読み出すようなことが行われるとは考えにくい、最適化結果としてそういうことがあるかもしれないとするとダメだが、パディングが一緒にコピーされることから考えるとメンバーに固有のサイズによって処理を変えているとは思えない）  
これって実装依存なのか？  
それともやはり動作未定義なのか？  
（それとも動作はするだろうけれども規格上動作未定義？）


CC BY-SA 3.0
2016-12-24T10:03:20.460
31352
2016-12-24T10:46:41.310
2016-12-24T10:46:41.310
5044
5044
31346
answer