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

liv*_*hak 13 c gcc pointers

我有一个unsigned char指针,其中包含一个结构.现在我想要执行以下操作

unsigned char buffer[24];

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

int len = ntohs((record_t*)buffer->len);
Run Code Online (Sandbox Code Playgroud)

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

error: request for member ‘len’ in something not a structure or union.
Run Code Online (Sandbox Code Playgroud)

然后我尝试了:

int len = ntohs(((record_t*)buffer)->len);
Run Code Online (Sandbox Code Playgroud)

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

然后我宣布

record_t *rec = null;

rec = (record_t*)
Run Code Online (Sandbox Code Playgroud)

我在这做错了什么?

Ker*_* SB 19

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

int a;
float * p = (float*)&a;  // #1
float b = *p;            // #2
Run Code Online (Sandbox Code Playgroud)

这里#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!
Run Code Online (Sandbox Code Playgroud)

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


Mys*_*ial 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);
Run Code Online (Sandbox Code Playgroud)

编辑:

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

  • @AndreyT - C99 TC3使得对工会进行类型惩罚是合法的.(我不能引用它,但请查看http://stackoverflow.com/questions/7411233/can-we-use-va-arg-with-unions,了解一些同意我的人.) (6认同)