我是否通过创建虚拟结构数据类型违反了严格的别名规则?

Am I violating strict aliasing rules by creating dummy struct data types?

我有这两个功能:

static inline void *ether_payload(void *pkt)
{
  return ((char*)pkt) + 14;
}
static inline uint16_t ip_id(const void *pkt)
{
  const char *cpkt = pkt;
  uint16_t id;
  memcpy(&id, &cpkt[4], sizeof(id));
  return ntohs(id);
}

现在,存在类型安全问题。对于第一个函数,void 指针表示以太网 header。对于第二个函数,void 指针表示 IPv4 header。这产生了巨大的可能性,即有人不小心直接为以太网 header 调用了第二个函数。如果有人这样做,编译器不会发出警告。

我想通过两个内容从未定义的虚拟结构来消除此类型安全问题:

struct etherhdr;
struct ipv4hdr;

现在函数将是:

static inline struct ipv4hdr *ether_payload(struct etherhdr *pkt)
{
  return (struct ipv4hdr*)(((char*)pkt) + 14);
}
static inline uint16_t ip_id(const struct ipv4hdr *pkt)
{
  const char *cpkt = (const char*)pkt;
  uint16_t id;
  memcpy(&id, &cpkt[4], sizeof(id));
  return ntohs(id);
}

这解决了类型安全问题。请注意,我实际上并没有通过结构访问以太网或 IP headers,这确实是非常糟糕的做法。

我的问题是,我定义这样的 API 是否违反了严格的别名规则?请注意,永远不会通过结构访问数据;仅使用 char 指针通过 memcpy 访问数据。我的理解是 char 指针可以别名任何东西。

让我们将以太网数据包可以包含 IPv6 的事实视为无关紧要,因为这只是一个非常简单的示例。

至于回答你的问题,玉米杆已经回答了,不,你没有违反任何严格的别名规则。
您可以将指针转换为 char 指针。如果您确定这个另一个指针确实存在,您可以将 char 指针转换为另一个指针。 参见 Strict aliasing rule and 'char *' pointers

该标准允许实现对比其中包含的任何项目更粗糙的结构施加对齐限制。这将允许实现仅支持对齐访问的平台,例如

#include <string.h>
#include <stdint.h>
struct foo {uint32_t dat[1]; };
struct bar {uint16_t dat[2]; };
void test1(struct foo *dest, struct foo *src)
{
    memcpy(dest, src, 4);
}
void test2(struct bar *dest, struct bar *src)
{
    memcpy(dest, src, 4);
}

test2 生成与 test1 一样高效的代码 [使用一次 32 位读写,而不是两次 16 位读写]。如果一个实现总是将所有结构填充为四字节的倍数并将它们对齐到四字节边界,那么这样的实现将被允许在 test2 上执行上述优化,而不必知道或关心如何或者即使 struct bar 曾经在任何地方定义过。

我不知道目前的实现是否会做这样的事情,但我很难排除未来的实现可能会这样做的可能性,因为在某些情况下它可以允许更高效的代码生成。