网络通信编程中,常常用结构体对待发送的数据进行封装。比如,
struct msg { charcmd; char len; int extraData[0]; };
cmd表示命令,远端会根据cmd进行不同的处理;extraData是要发送的数组;len则表示extraData的占用的内存字节数。
在这种设计下,当需要发送数据时,把根据数据长度增加整个struct msg的内存(一般是定义struct msg指针,然后用realloc函数重新分配),然后把待发送数据填入extraData指向的内存单元(就是新增加的内存单元)。再把数据长度填入len,命令填入cmd。
struct msg *clientMsg = (struct msg *)malloc(sizeof(structmsg));
clientMsg->cmd =CMD_COMPUTE;
clientMsg->len = 12;
clientMsg = (struct msg *)realloc(clientMsg, sizeof(struct msg) + 12 );
*(clientMsg->extraData) =11;
*(clientMsg->extraData + 1) = 22;
*(clientMsg->extraData + 3) = 33;
那么,send函数的第二个参数能否填该结构体的地址呢?
send函数原型为:
ssize_t send(int sockfd, const void*buf, size_t len, int flags)
其意义是发送buf指向的内存的连续的len个字节单元。结构体的地址实质是一块连续内存单元的首地址,那么将其填在此处是符合逻辑的。
然而,考虑到结构体的内存分布(参考本人另一篇博客《struct内存对齐》),一个结构体的内存中很有可能存在无效的字节。64位cpu中,msg结构体在内存中的分布为
地址: 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf
数据:|-----------data[1]----------| |-----------data[2]----------|
占用单元数: 4 4
地址: 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 (共8个字节)
数据:|cmd| |len| |无效字节| |--------data[0]----------|
占用单元数: 1 1 2 4
地址0x02-0x03为无效字节。如果将&data和sizeof(struct data)填入send函数,发送的将是从0x0到0xb的内存单元,包含了无效字节。
基于发送端对发送数据的操作,远端接收数据时,必须先拿到len值,然后根据该值增加extraData指向的内存单元个数,才能接收数据。
struct msg *clientMsg = (struct msg *)malloc(sizeof(structmsg));
recv(sockfd, clientMsg,sizeof(struct msg));//此处取得cmd=CMD_COMPUTE和len =12
clientMsg = (struct msg *)realloc(clientMsg, sizeof(struct msg) + clientMsg->len );//根据len值增加内存
recv(sockfd, clientMsg->extraData,clientMsg->len);//接收extraData,此时会把发送端内存地址为0x02到0x0d的12个字节接收过来,然而,0x02到0x03为无效字节,且有效字节0x0e-0x0f没有被接收。
综上所述,在此种情况下,发送结构体时不可直接填入其地址。