解除引用类型惩罚指针将破坏严格别名规则

我有一个包含结构的unsigned char指针。现在我想要执行以下操作

unsigned char buffer[24]; //code to fill the buffer with the relevant information. int len = ntohs((record_t*)buffer->len); 

其中record_t结构包含一个名为len的字段。我无法这样做并且收到错误。

 error: request for member 'len' in something not a structure or union. 

然后我尝试了:

 int len = ntohs(((record_t*)buffer)->len); 

这样才能使操作员优先。 这给了我warning: dereferencing type-punned pointer will break strict-aliasing rules

然后我宣布

 record_t *rec = null; rec = (record_t*) 

我在这做错了什么?

根据C和C ++标准,通过指向另一种类型的指针访问给定类型的变量是未定义的行为 。 例:

 int a; float * p = (float*)&a; // #1 float b = *p; // #2 

这里#2导致未定义的行为。 #1的分配称为“类型双关语”。 术语“混叠”是指几个不同的指针变量可能指向相同数据的想法 – 在这种情况下, p对数据a别名。 法律别名是优化的问题(这是Fortran在某些情况下表现出色的主要原因之一),但我们在这里得到的是非法混淆。

你的情况也不例外; 您通过指向不同类型的指针(即不是char *的指针)访问buffer数据。 这根本不允许。

结果是:你应该首先在buffer中没有数据。

但是如何解决呢? 确保你有一个有效的指针! 类型惩罚有一个例外,即通过指向char的指针访问数据,这允许的。 所以我们可以这样写:

 record_t data; record_t * p = &data; // good pointer char * buffer = (char*)&data; // this is allowed! return p->len; // access through correct pointer! 

关键的区别在于我们将实际数据存储在正确类型的变量中,并且只有在分配了该变量之后才将变量视为字符数组(这是允许的)。 这里的道德是字符数组总是第二,真正的数据类型是第一位的。

您正在收到警告,因为您通过指向同一位置的两个不同类型的指针来打破严格别名。

解决这个问题的一种方法是使用工会:

 union{ unsigned char buffer[24]; record_t record_part; }; //code to fill the buffer with the relavent information. int len = ntohs(record_part.len); 

编辑:

严格来说,这并不比原始代码安全得多,但它并没有违反严格别名。

你可以试试这个:

 unsigned char buffer[sizeof(record_t)]; record_t rec; int len; // code to fill in buffer goes here... memcpy(&rec, buffer, sizeof(rec)); len = ntohs(rec.len); 

你可能有一个警告级别设置,包括严格的别名警告(它曾经不是默认值,但在某一点gcc翻转了默认值)。 尝试-Wno-strict-aliasing-fno-strict-aliasing – 然后gcc不应该生成警告

一个相当不错的解释(基于粗略的一瞥)是什么是严格的混叠规则?