将char数组转换为struct*类型

4
在下面的代码中,有人可以解释一下struct ether_header *eh = (struct ether_header *) sendbuf;这行发生了什么吗?我理解它创建了一个指针eh,类型为ether_header,在右手边,你将sendbuf强制转换为struct ether_header的指针。但是如果sendbuf是一个char array,你怎么能做到这一点呢?另外为什么要这样做?
以下是完整代码链接:发送以太网帧
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/ether.h>

int main(int argc, char *argv[])
{
    int sockfd;
    struct ifreq if_idx;
    struct ifreq if_mac;
    int tx_len = 0;
    char sendbuf[BUF_SIZ];
    struct ether_header *eh = (struct ether_header *) sendbuf;

2
发生了什么?代码正在违反字符串别名规则。 - Andrew Henle
2
如果你提供代码的下几行,那就更容易解释了。 - user2736738
1
@AndrewHenle,您确定这违反了字符串别名吗? - harper
2
@harper 技术上讲,char [] 应该算是字符串的一种形式... 但是没错,这是个笔误;应该是“strict aliasing”。 - Christian Gibbons
3
struct ether_header *eh = (struct ether_header *) sendbuf; 是未定义行为。避免使用这种编码风格。 - chux - Reinstate Monica
显示剩余3条评论
2个回答

3

但是如果sendbuf是一个char数组,你怎么做呢?

代码不应该这样做。

将指针强制转换为原本不是该类型的有效指针类型是未定义行为(UB)。

char sendbuf[BUF_SIZ];
struct ether_header *eh = (struct ether_header *) sendbuf;  // UB

至少要考虑一下,如果struct ether_header需要对齐到偶数地址,而sendbuf[]从奇数地址开始。赋值可能会导致程序崩溃。

第二个问题是未发布的代码可能会如何处理sendbuf[]eh,这可能会违反严格别名规则@Andrew Henle


更好的方法是使用一个 union。现在成员已对齐,union 处理了严格别名规则。
union {
  char sendbuf[BUF_SIZ];
  struct ether_header eh;
} u;

还有,你为什么要这样做?

为了从两种数据类型的角度访问数据。也许是为了对 u 进行数据转储。


@alk 处理了对齐问题。但是,sendbuf[] 的大小要求可能超过 sizeof(eh)。OP 在这一点上不太清楚。此外,由于抗锯齿/优化,eh 中的更改可以在 sendbuf 中检测到,但反之则不一定。AA 规则具有代码可以通过 union 避免的微妙之处。 - chux - Reinstate Monica
@coderredoc 这是派生的。如果代码执行了C规范没有指定的操作,那么它就是未定义行为。“或者由于任何明确行为定义的省略。”C11 §4 2。所以问题变成了,在C规范的哪里说允许将任何char *指针强制转换为struct some_struct *?由于缺乏这方面的规定,代码就是未定义行为。 - chux - Reinstate Monica
事实是,一个人可以转换指针,但丑陋的事情是,当通过“错误”类型的指针访问数据时,可能会出现微妙的陷阱引发UB。@coderredoc - alk
通常允许的唯一情况是通过char指针逐字节访问任何对象(在其边界内)。@coderredoc(如此评论所建议的)。 - alk
1
@alk 我怀疑这是任何字符指针。指向“无符号字符”的指针有不会出现陷阱的优点。 - chux - Reinstate Monica
显示剩余7条评论

0

代码中的 char sendbuf[BUF_SIZ] 分配了一个 char 类型(在大多数系统上为字节)的块,而转换 struct ether_header *eh = (struct ether_header *)sendbuf 表示您明确希望将其视为 struct ether_header 类型。这个转换没有什么重要的指令,除了(可能)设置一个CPU寄存器。

最终您将得到两个指向同一块内存的指针。修改其中一个指针将会影响另一个指针。

话虽如此,这个做法并不完全正确/安全,因为 sendbuf 可能没有适当地对齐以实际包含一个 struct ether_header

编辑:关于结构体别名规则,char* 明确允许与任何其他数据类型别名,但反之未必成立。


7
“然而,这并不违反别名规则,因为char*显式地被允许与任何其他数据类型发生别名。” 这不是指“char *”别名另一种数据类型,而是指另一种数据类型别名了“char *”。 - Andrew Henle
1
如果 struct ether_header * 具有比 char * 更强的对齐要求,则 struct ether_header *eh = (struct ether_header *) sendbuf 可能会引起问题。这个赋值是未定义行为。 - chux - Reinstate Monica
@AndrewHenle,简单地将指针强制转换会创建双向别名。它是否违反了严格别名规则取决于它们如何被解引用,即是先写入char再读取结构体,还是先写入结构体再读取char。由于OP没有指示这些指针的使用方式,因此我们实际上无法确定这里是否存在严格别名违规问题。 - Joe Hickey
@chux,您能否澄清赋值本身是否是未定义行为,还是实际使用别名是未定义行为。 - MFisherKDX
2
OP没有指明这些指针的使用方式,因此我们实际上无法看到是否存在严格别名违规。不,这是严格别名违规。我们看不到的是结果UB是否会导致问题。只有在x86平台上编码的程序员往往不理解违反严格别名和其他类型数据对齐限制所造成的问题。x86非常宽容任何类型的未对齐内存访问。[其他硬件?就不是这样了。] (https://www.google.com/search?q=SIGBUS+SPARC) - Andrew Henle
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接