Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/56.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 我是否通过创建虚拟结构数据类型违反了严格的别名规则?_C_Strict Aliasing - Fatal编程技术网

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

C 我是否通过创建虚拟结构数据类型违反了严格的别名规则?,c,strict-aliasing,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); } 现在,有一个类型

我有两个功能:

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是否违反了严格的别名规则?注意:数据从不通过结构访问;数据只是使用char指针通过memcpy访问的。我的理解是,char指针可以指向任何对象


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

至于回答您的问题,Cornstales已经回答了,不,您没有违反任何严格的别名规则。
您可以将指针转换为字符指针。您可以将char指针转换为另一个指针,如果您确信另一个指针确实存在的话。
参见

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

#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);
}
#包括
#包括
结构foo{uint32_t dat[1];};
结构条{uint16_t dat[2];};
void test1(结构foo*dest,结构foo*src)
{
memcpy(dest、src、4);
}
void test2(结构条*dest,结构条*src)
{
memcpy(dest、src、4);
}
test2
生成代码,其效率与
test1
相同[使用一个32位读写,而不是两个16位读写]。如果一个实现总是将所有结构填充到四个字节的倍数,并将它们与四个字节的边界对齐,那么这样一个实现就可以在
test2
上执行上述优化,而不必知道或关心
struct bar
是如何定义的,甚至是否在任何地方定义的


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

char*
确实允许使用alias。我没有看到任何严格的别名冲突。关于你的建议。。。如果您使用typedef void ipv4hdr,那么ip4hdr*将是一个空指针,类型与其他空指针(如etherhdr*)兼容。因此,伪结构是必要的。为了扩展@juhist所写的内容:在您的建议中,误用不会给出编译器警告,这就是重点。