解除引用类型惩罚指针将破坏严格别名规则
我有一个包含结构的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不应该生成警告
一个相当不错的解释(基于粗略的一瞥)是什么是严格的混叠规则?