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

时间:2018-04-08 19:51:05

标签: c strict-aliasing

我有这两个功能:

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指针表示以太网头。对于第二个函数,void指针表示IPv4标头。这极有可能导致某人意外地直接调用以太网头的第二个函数。如果有人这样做,编译器就不会发出警告。

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

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头,这实际上是非常糟糕的做法。

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

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

1 个答案:

答案 0 :(得分:1)

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

[建议]因为这些类型(ipv4hdr和etherhdr)不是真正的结构(你没有访问他们的任何成员,正如一些人所期望的那样,你只使用它们作为指针)并且你关注一些严格别名,也许最好使用void typedef来简单地隐藏底层类型。一些用户可能希望,struct ipv4hdr或struct etherhdr具有相同的可访问成员,但他们没有,这对我来说似乎很奇怪。以下代码看起来更好,因为我知道typedefs ipv4hdr和etherhdr是我只能通过你的函数访问的变量(但也许只是我):

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