免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 46041 | 回复: 88
打印 上一主题 下一主题

char[0], sizeof和struct padding [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-05-17 00:26 |只看该作者 |倒序浏览
我想举一个自己最近在项目中犯的错误来说明要踏踏实实做人,不要做装B青年
在代码中,我需要在一个library和一个daemon之间通过socket传送数据包,包的格式定义如下(为了简化,我就用最简单的数据类型举例):

  1. typedef struct {
  2.         int head;
  3.         int size; //指明整个包的长度
  4.         char reply;
  5.         char data[0];
  6. } packet;

  7. packet*  cmd = malloc (sizeof(packet) + 20);
  8. memcpy (packet->data, some_data, 20);
复制代码


daemon将上面分配的cmd包发送给library,library接收到包后,需要将data字段中的数据取出来。size指明了整个包的长度,但没有字段指明数据的长度。我需要这么一个指明数据长度的字段吗?作为一个装B青年,我认为当然不需要,于是我这样来计算数据的长度:

  1. #define offsetof(type, element) ((int)&((type *)0)->element)
  2. static inline size_t packet_data_len(packet* cmd) {
  3.     assert(cmd);
  4.     return cmd->size - offsetof(packet, data);
  5. }

  6. memcpy (buffer_to_receive_data, cmd->data, packet_data_len (cmd));
复制代码


于是乎,这段程序成功的给我带来了无数的bug,莫名奇妙的segfault,奇怪的数据错误,还是有部分时间的正常工作。当然,最终我还是找到了问题:
sizeof (packet) == 12;
这是合理的,char reply被padding成了4个字节,而char data[0]字节为0。
但,offsetof(packet, data) == 9,在计算偏移时,char reply为一个字节,没有padding。
所以packet_data_len每次都会返回比真实的数据多3个字节 ……

最后我还是老老实实加了个data_len字段指明数据的长度,并告诫自己,本本分分写程序,莫装B ……

论坛徽章:
0
2 [报告]
发表于 2009-05-17 00:31 |只看该作者
我常讲的,代码的逻辑清晰要比任何小技巧要重要得多。
最简单方法就是最有效的方法。
拒绝淫技,当然 linux kernel 除外,偶不敢批

论坛徽章:
0
3 [报告]
发表于 2009-05-17 00:41 |只看该作者
学习,谢谢楼主分享经验

论坛徽章:
0
4 [报告]
发表于 2009-05-17 00:47 |只看该作者
原帖由 mik 于 2009-5-17 00:31 发表
我常讲的,代码的逻辑清晰要比任何小技巧要重要得多。
最简单方法就是最有效的方法。
拒绝淫技,当然 linux kernel 除外,偶不敢批

是的,我也从来都是这样个别人说的。
但自己却从来喜欢用些奇技淫巧,惭愧

论坛徽章:
0
5 [报告]
发表于 2009-05-17 16:46 |只看该作者
还是老老实实的比较好,起码能让逻辑看上去比较清晰明了

论坛徽章:
0
6 [报告]
发表于 2009-05-17 18:08 |只看该作者
原帖由 zx_wing 于 2009-5-17 00:26 发表
我想举一个自己最近在项目中犯的错误来说明要踏踏实实做人,不要做装B青年
在代码中,我需要在一个library和一个daemon之间通过socket传送数据包,包的格式定义如下(为了简化,我就用最简单的数据类型举例 ...

这算鸟奇技淫巧。
LZ既然用到GCC的零长数组,就应该仔细阅读结构布局的说明。显然LZ没仔细搞清楚GCC的C扩展就乱用。
要是嫌阅读说明麻烦,把最后的data[0]改成data[1]不什么事都没了嘛。

论坛徽章:
0
7 [报告]
发表于 2009-05-17 18:24 |只看该作者
原帖由 yhb04 于 2009-5-17 18:08 发表

这算鸟奇技淫巧。
LZ既然用到GCC的零长数组,就应该仔细阅读结构布局的说明。显然LZ没仔细搞清楚GCC的C扩展就乱用。
要是嫌阅读说明麻烦,把最后的data[0]改成data[1]不什么事都没了嘛。

看贴不仔细, 改成data[1]不解决问题

论坛徽章:
0
8 [报告]
发表于 2009-05-17 18:58 |只看该作者
原帖由 baicj 于 2009-5-17 18:24 发表

看贴不仔细, 改成data[1]不解决问题

???
怎么不解决问题?说来听听。

论坛徽章:
0
9 [报告]
发表于 2009-05-17 19:11 |只看该作者
typedef struct {
        int head;
        int size; //指明整个包的长度
     char reply;
        char data[1];
} packet;

packet*  cmd = malloc (sizeof(packet) + 20); //多空几个字节无所谓
memcpy (packet->data, some_data, 20);

#define offsetof(type, element) ((unsigned long)&((type *)0)->element)
cmd->size = offsetof(packet, data) + 20;//cmd->size的计算不要用sizeof(packet)



static inline size_t packet_data_len(packet* cmd) {
    assert(cmd);
    return cmd->size - offsetof(packet, data);
}

memcpy (buffer_to_receive_data, cmd->data, packet_data_len (cmd));




或者直接用cmd->size记录实际数据的长度,数据在cmd->data[0]处开始。

论坛徽章:
0
10 [报告]
发表于 2009-05-17 19:58 |只看该作者
原帖由 yhb04 于 2009-5-17 19:11 发表
...
#define offsetof(type, element) ((unsigned long)&((type *)0)->element)
cmd->size = offsetof(packet, data) + 20;//cmd->size的计算不要用sizeof(packet)
...

如同你所说, 问题在于daemon端用sizeof计算的struct head大小, 而library端用offsetof计算struct head大小, 两者结果不一致. 修改位data[1]不会有帮助, 解决问题的办法也正如你的代码, 统一用offsetof来代替sizeof, 或者用sizeof来代替offsetof.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP