【C言語】
memcmpで構造体比較すると
穴に落ちる(padding)

warning: padding struct to align ‘i2’

警告:i2′ を整列するため構造体に詰め物した
[-Wpadded]


■この記事の概要

この記事では、C言語でmemcmpを使った構造体の比較に関する問題を解説しています。構造体内のパディング(詰め物)が比較に影響を及ぼし、予期しない動作を引き起こす可能性があります。具体例を挙げ、比較時の注意点や安全な手法について説明しています。


■境界調整のために穴が開く

構造体のメンバの割り付けはコンパイラ任せなので
各メンバがピッタリ隙間なく割り付けられるとは限りません。
境界調整のために穴が開く場合があり
穴に何が書かれているかは誰もわかりません。

■構造体パディングの例

#include    <stdio.h>
#include    <string.h>
#include    <stdint.h>
struct  hole    {
    uint32_t    i1 ;
    uint8_t     c  ;
    // 3byteの穴    
    uint32_t    i2 ;
} ;
//構造体をダンプする
static  void    memdmp(uint8_t *d1,uint8_t *d2,size_t size){
    for(size_t i = 0 ; i < size ;i++){
        if(d1[i] == d2[i]){
            continue;
        }
        printf("d1[%2zd]=%2x d2[%2zd]=%2x\n",
            i,d1[i],
            i,d2[i]
        );
    }
}
//メンバの内容が同じ構造体を比較する
void    cmp(void){  
    struct  hole 自動変数 = {
        .i1 = 0x12345678,
        .c  = 0xab,
        .i2 = 0x87654321
    };
    static struct hole 静的変数 = {
        .i1 = 0x12345678,
        .c  = 0xab,
        .i2 = 0x87654321 
    };
    int ret = memcmp(&自動変数,&静的変数,sizeof(自動変数)) ;
    printf("memcmpの結果=%d\n",ret) ;
    if(ret != 0){
        memdmp((uint8_t *)&自動変数,(uint8_t *)&静的変数,sizeof(自動変数));
    }
}
//自動変数領域をFFで汚す
void    ff(void){
    struct hole ff;
    memset(&ff,0xFF,sizeof(ff));
} 
int main(void){ 
    ff(); //自動変数領域をFFで汚す
    cmp();  
}

構造体の詰め物(=穴=パディング)の内容は
未規定(=コンパイラ依存)なので、
このプログラムの7行目の【穴】に何が入っているのか
わかりません。

➡筆者環境下gcc最適化無しの実行結果

./a.out
memcmpの結果=12
d1[ 5]=ff d2[ 5]= 0
d1[ 6]=ff d2[ 6]= 0
d1[ 7]=ff d2[ 7]= 0

➡筆者環境下gcc最適化有りの実行結果

./a.out
memcmpの結果=0

参考:

EXP04-C. 構造体を含むバイト単位の比較を行わない

https://www.jpcert.or.jp/sc-rules/c-exp04-c.html

C-FAQ 2.8: なぜ構造体を比較することはできないのか

http://www.kouno.jp/home/c_faq/c2.html#8