2015-11-04 124 views
1

在我们公司中,通过网络发送C/C++结构非常普遍。任何结构具有一个或多个uint32_t的字段:通过网络发送结构

typedef struct { 
    uint32_t data1; 
    uint32_t data2; 
} Data; 

和字段(数据1,数据2,...)封装,我们需要发送的真实数据。我们的数据大部分是单个或几个比特长,所以为了节省空间/带宽,它们在特定比特位置的这些字段内对齐。

要访问我们的真实数据,我们编写了“干将”和“二传手宏与bitshifting和位掩码(struct的外面!):

#define READY_MASK 0x01  // indicates that READY is a single bit value 
#define READY_OFFSET 3  // indicates position of READY inside 32-bit field 
#define IS_READY(x) { ... } // returns READY value from x field 
#define SET_READY(x,r) { ... } // sets READY value to x field 

现在我想修改,简化和使这个过程更安全通过添加getter和setter直接进入结构,例如:

typedef struct { 
    uint32_t data1; 

    #define READY_MASK 0x01 
    #define READY_OFFSET 3 
    inline void set_ready(uint32_t r) { /*...*/ } 
    inline uint32_t is_ready() { /*...*/ } 
    // lots of other getters and setters 
} Data1; 

至于我的实验是正确的,我已经注意到,这种修改不以大小的影响结构sizeof(Data)==sizeof(Data1),我可以发送这个结构网络以及另一端的接收和解码。

我的问题是:在这个修改中有什么不正确的吗?有什么危险的或我应该知道的任何事情?

+2

那'Data1'结构不是C结构,C结构不能有成员函数。你确定你不用C++编程吗? –

+0

假设它是C++(因为它只是普通的不会在C编译...),那么如何将函数添加到结构使事情更安全? –

+0

当然@JoachimPileborg,Data1是C++ – crooveck

回答

1

原则上,如果不添加虚拟函数,将不会有vptr和struct size与没有成员函数的情况相同。但它不再是一个POD结构,所以你可以很容易地进入UB的未定义行为的黄昏区域。由于C++还没有标准化的ABI,所以通常采用C型POD结构是很好的。通过网络进行序列化。为C++模块/子系统制作精简的可移植API也同样适用于C ABI。另外,如果你考虑这些(如一些评论者的建议),我建议你远离位域,手动位掩通常更容易分析和制作便携。比特场可以例如给你排序问题,这至少部分是因为结构'始终'的第一个成员结束于struct start的较低内存地址,而不管字节顺序如何。位域如何工作还可能取决于传输介质,例如, BSD网络流套接字是一个字节流,但许多硬件接口复制整个32位字大小的块而不是字节。

2

您所做的修改不会造成任何伤害,但它也不会产生任何效果,因为无论如何struct结构都是公共的。之所以Data和Data1是相同的大小,是因为你添加的是内联函数,它不占用对象中的空间,而是用实际函数代码替换任何函数调用。现在

,如果您是通过网络发送结构和二进制数据,必须考虑以下两个原则:

  • 按照惯例整数使用网络字节顺序是相对于86的小大端发送尾段。如果您确定所有发送和接收数据的机器都是x86,则这不是问题。但是,如果任何机器具有不同的排列顺序,如Big Endian或No Endian(或Middle Endian),则会出现问题。例如,你可以有一个ARM处理器,SPARC等在C你有以下宏:

    还有ntohs,htons,再用ntohl,htonl

  • 你还必须考虑不同的记忆比对。内存对齐的结构取决于体系结构,编译器和编译模式。编译器可能会添加填充(它们不能在C中重新排序成员,但请阅读:Can a C++ compiler re-order elements in a struct)。此外,像32位和64位体系结构这样的类型在长度上有不同的大小。即使可以肯定,只有两个unsigned int成员不会有问题,但您绝对不应该从结构体向存储器副本发送消息中发送的数据缓冲区,也不应将消息中的原始数据从存储器副本复制到一个结构。基本上,你不应该这样做:

    字符缓冲区[BUF_SIZE]

    Data1 myData;

    的memcpy(缓冲液,myData的,的sizeof(数据));

https://en.wikipedia.org/wiki/Data_structure_alignment

你应该将会员缓冲一个接一个。从消息中收到的原始数据填充结构时也是如此。

我也建议不要使用位域

,读Why bit endianness is an issue in bitfields?。但要非常清楚,在你的片段中,你不使用位域。使用位域与使用整数中的位来表示某些特定状态不同。在这种情况下,您做得很好,应该只考虑具有不同字节顺序的体系结构中的整数表示(如果这是可能的情况)。