C语言 如何正确地将字符数组类型转换为结构指针?

pbwdgjma  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(124)

我是C语言新手。下面的代码片段来自我阅读的一本书。

struct S {
  int i; double d; char c;
};

int main(void) {
  unsigned char bad_buff[sizeof(struct S)];
  _Alignas(struct S) unsigned char good_buff[sizeof(struct S)];

  struct S *bad_s_ptr = (struct S *)bad_buff;   // wrong pointer alignment
  struct S *good_s_ptr = (struct S *)good_buff; // correct pointer alignment
}

作者指出bad_buff可能对成员访问表达式有不正确的对齐。我很难理解这个声明。如果我们使用bad_buff,会出现什么问题?

46scxncf

46scxncf1#

正确答案是:如果你是一个C语言的初学者,你就在深水沃茨,因为这是一个相当高级的主题。我的经验法则是,初学者不应该出于任何目的使用强制转换操作符,因为这样做有很多陷阱。
从原始字符字节数组到结构类型有两个问题:

  • 首先是代码稍微处理的对齐问题。但是_Alignas等只确保缓冲区和结构体具有相同的对齐方式,即起始地址。它不对单个结构成员的对齐以及结构内部可能包含的任何填充进行任何操作。这些东西是不可携带的。
  • 其次,这种脏类型的转换在C语言中是“未定义的行为”,这意味着任何事情都可能发生。包括生成不正确的代码,抛出硬件异常等。或者,它可能只是 * 似乎 * 工作现在/这一次/永远/直到下一个满月。What is the strict aliasing rule?

(As你可以安全地从一个结构体转换到一个字符字节数组,但不能反过来。
没有快速解决方案来解决这两个问题。如果你知道一个结构体有填充字节以及它们在哪里,那么你可以这样做:

typedef union
{
  some_struct s;
  unsigned char buf [sizeof(some_struct)];
} some_union;

这解决了严格的别名问题和初始对齐问题,但它没有解决单个结构成员可能具有填充字节的问题。为此,您需要一些非标准的方法,如#pragma pack(1)。这反过来又是有问题的,因为填充是有原因的,如果你打包了结构,你可能无法按预期访问某些成员。
结构体总体上是不可移植的,不适合接近金属结构的东西,如数据通信协议或寄存器Map。唯一真正可移植的方法是编写一个序列化/重复化函数,该函数逐个复制单个结构体成员。

相关问题