结构成员的地址是否定义了行为?

Is address of structure members defined behavior?

有时我对结构进行指针运算,但我不确定它是否是已定义的行为..

如果我有这个结构

typedef struct SCustomData{
    uint32_t a;
    uint32_t b;
}CustomData;

有时对我来说,与其不断地输入结构的不同字段,不如给它一个恒定的大小并适合我需要的任何内容。

我的第一个问题是:结构的地址也是结构中第一个变量的地址是否定义了行为?

int main(){
    CustomData cd = {0};
    cd.a = 0xDEADBEEF;
    
    uint32_t a = 0;
    
    memcpy(&a,&cd,sizeof(uint32_t));
    
    assert(a==0xDEADBEEF);
    
    
    return 0;
}

那会永远成功吗?

我的下一个问题是,只要我知道每个成员的大小,我可以使用指针算法访问结构的任何成员吗?

int main(){
    CustomData cd = {0};
    
    cd.b = 0xABCD0000;
    uint16_t highshort = *(uint16_t*)((uint8_t*)&cd+sizeof(uint32_t)+sizeof(uint16_t));
    
    assert(highshort == 0xABCD);
    
    uint8_t highbyte = *(uint8_t*)((uint8_t*)&cd+sizeof(uint32_t)+sizeof(uint16_t)+sizeof(uint8_t));
    
    assert(highbyte == 0xAB);
    
    return 0;
}

除了可能看起来丑陋或糟糕的代码实践之外,至少定义了行为?我应该避免它吗?

如果我不使用指针转换而是使用 memcpy,这仍然是个坏主意吗?

int main(){
    CustomData cd = {0};
    cd.b = 0xABCD0000;
    uint16_t highshort = 0;
    memcpy(&highshort, ((uint8_t*)&cd)+sizeof(uint32_t)+sizeof(uint16_t), sizeof(uint16_t));
    
    assert(highshort == 0xABCD);
    
    return 0;
}

第一个问题

它实际上是在C标准中定义的!第6.7.2.1节(在此standard draft

15 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.7.2.1节):

Each non-bit-field member of a structure or union object is aligned in an implementation- defined manner appropriate to its type.

然而,大多数编译器以相同的方式对齐(per-architecture 和位数至少)

为什么它仍然是个坏主意

  • 可维护性 - 如果您使用成员本身,您的代码将更容易维护。它不会依赖于编译器和位数,并且不会对代码中的更改敏感(例如更改类型或重新排序成员)。

  • 可读性——你的意图当然会更加清晰。

  • Sometimes compilers do weird stuff ;)