C 取消引用类型双关指针将打破严格的别名规则

C 取消引用类型双关指针将打破严格的别名规则,c,pointers,gcc,C,Pointers,Gcc,我有一个包含结构的无符号字符指针 unsigned char buffer[24]; //code to fill the buffer with the relevant information. int len = ntohs((record_t*)buffer->len); 其中记录结构包含一个名为len的字段。我无法这样做,因此得到了错误 error: request for member ‘len’ in something not a structure or union

我有一个包含结构的无符号字符指针

unsigned char buffer[24];

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

int len = ntohs((record_t*)buffer->len);
其中记录结构包含一个名为len的字段。我无法这样做,因此得到了错误

error: request for member ‘len’ in something not a structure or union.
然后我试着:

int len = ntohs(((record_t*)buffer)->len);
以便获得正确的运算符优先级。这给了我一个
警告:
取消引用类型双关指针将打破严格的别名规则

然后我宣布

record_t *rec = null;

rec = (record_t*)

我做错了什么?

您可能设置了一个警告级别,其中包括严格的别名警告(它过去不是默认值,但在某一点上gcc改变了默认值)。尝试
-Wno严格别名
-fno严格别名
——然后gcc不应生成警告


一个合理的解释(基于粗略的一瞥)是

你得到了这个警告,因为你有两个不同类型的指针指向同一个位置,从而打破了严格的别名

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

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

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

int len = ntohs(record_part.len);
编辑:


严格地说,这比原代码安全得多,但是它不违反严格的别名。

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

int a;
float * p = (float*)&a;  // #1
float b = *p;            // #2
这里#2导致未定义的行为。#1处的赋值称为“类型双关”。术语“别名”指的是几个不同的指针变量可能指向相同的数据——在这种情况下,
p
为数据
a
添加别名。合法别名是一个优化问题(这是Fortran在某些情况下性能优越的主要原因之一),但我们这里的问题是彻底的非法别名

你的情况也一样;您正在通过指向不同类型的指针(即不是
char*
的指针)访问
缓冲区中的数据。这是绝对不允许的

结果是:首先,您不应该在
缓冲区中有数据

但如何解决呢?确保你有一个有效的指针!类型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!
关键的区别在于,我们将实际数据存储在正确类型的变量中,并且只有在分配了该变量之后,我们才将该变量视为字符数组(这是允许的)。这里的寓意是字符数组总是排在第二位,真正的数据类型排在第一位。

您可以尝试以下方法:

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

你的编译器选项是什么?另外,关注手头的问题,不要告诉我们您以前遇到的语法错误。在C中,它是
NULL
,而不是
NULL
。警告是否可能重复,OP的代码可能是未定义的行为,这不应该被忽略。我正在讨论是否要-1这个。就如何禁用危险UB警告以及添加选项以使UB代码“按预期”工作,向OP提供建议似乎没有建设性。特别是因为代码还有其他UB,因为对齐问题没有被捕获,选项也无法解决。它将很高兴在x86上工作,然后在其他ARCH上崩溃…@R。有许多习惯用法明确使用这种行为(特别是在网络代码中),并且在很长一段时间内,gcc默认禁用了警告,特别是为了网络代码不会产生大量警告:),因为我们正在讨论网络代码(假设基于
ntohs
调用),至少提到这个事实是有意义的。正如我所指出的,由于gcc无法解决的原因,这样的代码几乎肯定是错误的:未对齐。读入字符缓冲区,然后试图将该缓冲区解释为另一种类型,这从根本上说是错误的,这表明代码的作者不知道如何传递空指针to正确类型的实际对象,以
读取
/
接收
(或者在缓冲区较大的情况下,如何使用
memcpy
)@FooBah:你确定你看到的代码实际上没有做什么不同的事情吗?例如,有许多看起来相似的习惯用法,事实上是完全合法的。例如,访问不同结构的公共初始元素是可以的。如果数组是从des的现有变量强制转换的,那么写入字符数组也是可以的ired类型…魔鬼在这些事情的细节中非常重要。无序进入工会是一种未定义的行为。你不能“绕过”任何事情(而是通往坟墓的直接途径)。@Kerrek SB:你能解释一下“无序”是什么意思吗?是的,我知道union方法也未定义,但原始代码也是如此。它只允许读取上次写入的同一个union成员。不过,读取其他成员几乎肯定是解决方案所必需的。没有办法回避无效类型双关语;您真的必须开始使用正确类型的变量(见我的答案)。@Mystical,它可能应该是:int len=ntohs(record_part.len);@AndreyT-C99 TC3使使用联合进行类型双关合法化。(我不能引用它,但请检查一些同意我的人。)这实际上是正确的方法(不过不需要强制转换到
void*
)。将一种类型的数据重新解释为另一种类型的数据的正确方法是使用
memcpy
(而不是直接基于指针的重新解释或基于联合的重新解释)在对象之间复制该数据。我同意这是正确的(我给了它+1),但最好永远不要使用
无符号字符[]
首先,将数据直接读取到正确类型的变量中。+1不仅用于告诉OP出了什么问题,而且还显示了正确的方法!不,不,您正在检查并忽略了要点。通过其他类型访问对象