0

假设我有一些复杂的结构

struct icmphdr
{
     u_int8_t type;
     u_int8_t code;
     u_int16_t checksum;

    /* Parts of the packet below don’t have to appear */
    union
    {
        struct
        {
            u_int16_t id;
            u_int16_t sequence;

            // Type of ICMP message
            // Packet code
           // Datagram checksum
        } echo;

        u_int32_t gateway;

        struct
        {
            u_int16_t __unused;
            u_int16_t mtu;
        } frag;


   } un;

}; 

和一个

char buf[SIZE];//for some integer SIZE

这个演员表的意义和兴趣是什么?

ip=(struct icmphdr*)buf; //ip was formerly defined as some struct iphdr *ip;
4

2 回答 2

1

您的代码背后的可能情况是:

程序员想要创建一个数据协议并将各种内容表示为一个结构,以简化编程并提高代码的可读性。

底层 API 可能只允许基于字节的数据传输。这意味着该结构必须作为“字节块”传递。您的特定代码似乎是接收者:它有一大块原始字节,并声明这些字节中的数据对应于一个结构。

形式上和理论上,C 标准没有定义在指向不同数据类型的指针之间进行转换时会发生什么。从理论上讲,如果您这样做,任何事情都可能发生。但在实践/现实世界中,只要对数据的结构有某种保证,这种类型的转换就可以很好地定义。

这是您可能遇到问题的地方。许多计算机都有对齐要求,这意味着编译器可以在 struct/union 内的任何位置自由插入所谓的填充字节。这些填充字节可能在两个编译之间不一定相同,它们在两个不同的系统之间肯定也不相同。

因此,您必须确保发送方和接收方都没有启用填充,或者它们具有相同的填充。否则你不能使用结构/联合,它们会导致程序崩溃和烧毁。

确保不启用结构填充的快速而肮脏的方法是使用编译器选项,例如 non-standard #pragma pack 1,许多编译器通常支持该选项。

专业的、可移植的方法是添加一个编译时断言来检查结构的大小是否确实符合预期。使用 C11,它看起来像

static_assert(sizeof(struct icmphdr) == 
                (sizeof(uint8_t) + 
                 sizeof(uint8_t) + ... /* all individual members' types */ ), 
              "Error: padding detected");

如果编译器不支持static_assert,有几种方法可以用各种宏实现类似的东西,甚至是运行时assert()

于 2013-05-13T15:12:39.243 回答
0

这很糟糕。永远不要创建一个 char 缓冲区并将其转换为一个结构,因为对齐将是错误的(即,char 缓冲区将有一些随机的起始地址,因为字符串可以从任何地方开始,但是 int 需要/应该有地址的倍数大多数架构有四个)。

解决方案是不要做那样的讨厌的演员表。建立一个适当的联合,使其具有最严格的成员对齐,或者使用特殊元素强制对齐(如果必须)(请参阅 /usr/include/sys/socket.h 中 sockaddr_storage 的定义或类似的)。

插图

您在堆栈上创建一个缓冲区并将一些数据读入其中:

char buf[1024]; int nread = read(fd, &buf, sizeof(buf));

现在你假设缓冲区是结构:

CHECK(nread >= sizeof(struct icmphdr));
struct icmphdr* hdr = (struct icmphdr*)buf;
hdr->u.gateway; // probable SIGSEGV on eg Itanium!

通过将缓冲区重新解释为结构,我们绕过了编译器的检查。如果我们不走运,&hdr->u.gateway不会是四的倍数,并且在某些平台上将其作为整数访问会很麻烦。

解决方案的插图

strut iphdr hdr; int nread = read(fd, &hdr, sizeof(hdr));
CHECK(nread == sizeof(hdr));
hdr.u.gateway; // OK

让编译器帮助你。不要做怪诞的演员表。当你创建一个缓冲区时,告诉编译器你将要使用缓冲区做什么,这样它就可以为你把它放在内存中的正确位置。

于 2013-05-13T13:05:19.867 回答