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

时间:2011-10-03 00:25:29

标签: c pointers gcc

我有一个包含结构的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*)

我在这里做错了什么?

4 个答案:

答案 0 :(得分:19)

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

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

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

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

结果是:您首先应该从buffer处获得数据。

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

record_t data;
record_t * p = &data;          // good pointer
char * buffer = (char*)&data;  // this is allowed!

return p->len;                 // access through correct pointer!

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

答案 1 :(得分:5)

你得到的警告是因为你通过让两个不同类型的指针指向同一个位置来打破严格别名。

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

union{
    unsigned char buffer[24];
    record_t record_part;
};

//code to fill the buffer with the relavent information.

int len = ntohs(record_part.len);

编辑:

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

答案 2 :(得分:3)

你可以试试这个:

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);

答案 3 :(得分:0)

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

一个相当不错的解释(基于粗略的一瞥)是What is the strict aliasing rule?