免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 3668 | 回复: 14

[C] 百思不得其解的字符串问题 - 谜底揭开,编译器根据memcpy操作位数臆断内存分配 [复制链接]

论坛徽章:
1
水瓶座
日期:2014-03-20 18:21:14
发表于 2015-06-03 13:26 |显示全部楼层
本帖最后由 ecloud 于 2015-06-05 01:50 编辑
  1. /* Get RA and DEC from the mount*/
  2.           write(fd,":GEC#",5);
  3.         usleep(500000);
  4.         n = read(fd,RADEC,20);
  5.         RADEC[n] = 0;
  6.         /* Get DEC*/
  7.         dec_sign = RADEC[0];
  8.         memcpy(DEC,&RADEC[1],8);
  9.         DEC[8] = 0;
  10.         dec = atoi(DEC);
  11.         dec_d = dec/360000;
  12.         dec_m = (dec-dec_d*360000)/6000;
  13.         dec_s = (dec-dec_d*360000-dec_m*6000)/100;
  14.         /* Get RA*/
  15.         memcpy(RA,&RADEC[9],8);
  16.         RA[8] = 0;
  17.         ra = atoi(RA);
  18.         ra_h = ra/3600000;
  19.         ra_m = (ra-ra_h*3600000)/60000;
  20.         ra_s = (ra-ra_h*3600000-ra_m*60000)/1000;
复制代码
这段代码的逻辑是,从串口读取一个17位长字符串RADEC,第0位符号,1-8给DEC转整数,9-16给RA转整数。目前为止功能实现正常,但是……

以下是第一次gdb的结果

  1. 50                n = read(fd,RADEC,20);
  2. 1: RADEC = "\000\000\000\000H\214\001@\001\000\000\000\000\000\000\000\030\217\001@"
  3. (gdb)
  4. 51                RADEC[n] = 0;
  5. 1: RADEC = "+3240000000642750#\001@"
  6. (gdb)
  7. 53                dec_sign = RADEC[0];
  8. 1: RADEC = "+3240000000642750#\000@"
  9. (gdb)
  10. 54                if (RADEC[0]=='+') dec_sign = 1;
  11. 1: RADEC = "+3240000000642750#\000@"
  12. (gdb)
  13. 55                if (RADEC[0]=='-') dec_sign = -1;
  14. 1: RADEC = "+3240000000642750#\000@"
  15. (gdb)
  16. 56                memcpy(DEC,&RADEC[1],8);
  17. 1: RADEC = "+3240000000642750#\000@"
  18. (gdb)
  19. 57                DEC[8] = 0;
  20. 1: RADEC = "+3240000000642750#\000@"
  21. (gdb)
  22. 58                dec = atoi(DEC);
  23. 1: RADEC = "\000\063\062\064\060\060\060\060\060\060\060\066\064\062\067\065\060#\000@"
复制代码
也就是说从57行 DEC[8] = 0;开始,RADEC的第一个字节被清0

后来经过研究发现,是编译器根据memcpy的操作长度臆断用户需要的内存空间,少给DEC分配了一个字节的内存,并且跟RADEC紧密相连,造成了DEC[8]跟RADEC[0]是同一个地址!

惊出了我一身冷汗,幸亏我这里只错了一位,没有造成太大影响,并且很凑巧的发现了这个问题。不然的话后果不堪设想,内存泄露就是这么很“正常”的产生了
我没试过X86平台是不是也这个德行,但是就ARM平台的这种情况来看,我要对gcc说一百句草泥马,你TM用得着这么帮我省内存吗???

论坛徽章:
289
射手座
日期:2013-08-23 12:04:38射手座
日期:2013-08-23 16:18:12未羊
日期:2013-08-30 14:33:15水瓶座
日期:2013-09-02 16:44:31摩羯座
日期:2013-09-25 09:33:52双子座
日期:2013-09-26 12:21:10金牛座
日期:2013-10-14 09:08:49申猴
日期:2013-10-16 13:09:43子鼠
日期:2013-10-17 23:23:19射手座
日期:2013-10-18 13:00:27金牛座
日期:2013-10-18 15:47:57午马
日期:2013-10-18 21:43:38
发表于 2015-06-03 14:01 |显示全部楼层
DEC[8] = 0;后面没有打印呀,这“无法显示”指什么?

论坛徽章:
1
水瓶座
日期:2014-03-20 18:21:14
发表于 2015-06-03 19:50 来自手机 |显示全部楼层
本帖最后由 ecloud 于 2015-06-03 23:24 编辑

printf("%s\n",RADEC);显示为空,在第10行以后的话
吊诡的是后面对[9]到[16]的读取竟然没影响,所以怀疑跟编译器行为有关

论坛徽章:
1
水瓶座
日期:2014-03-20 18:21:14
发表于 2015-06-03 20:02 来自手机 |显示全部楼层
会不会是 -O2引起的?编译器判断RADEC不再有用就给释放了?那太深了,自动内存回收啊……

论坛徽章:
59
2015年亚洲杯之约旦
日期:2015-01-27 21:27:392015年亚洲杯之日本
日期:2015-02-06 22:09:41拜羊年徽章
日期:2015-03-03 16:15:432015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:50:282015元宵节徽章
日期:2015-03-06 15:50:392015年亚洲杯之阿联酋
日期:2015-03-19 17:39:302015年亚洲杯之中国
日期:2015-03-23 18:52:23巳蛇
日期:2014-12-14 22:44:03双子座
日期:2014-12-10 21:39:16处女座
日期:2014-12-02 08:03:17天蝎座
日期:2014-07-21 19:08:47
发表于 2015-06-03 22:23 |显示全部楼层
回复 4# ecloud


    你都Printf了, 谁敢说没用了?
不会你在GDB中Print的吧? 加上O2以后?
要是那样, 请自已到北韩申请狗决。












2

论坛徽章:
59
2015年亚洲杯之约旦
日期:2015-01-27 21:27:392015年亚洲杯之日本
日期:2015-02-06 22:09:41拜羊年徽章
日期:2015-03-03 16:15:432015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:50:282015元宵节徽章
日期:2015-03-06 15:50:392015年亚洲杯之阿联酋
日期:2015-03-19 17:39:302015年亚洲杯之中国
日期:2015-03-23 18:52:23巳蛇
日期:2014-12-14 22:44:03双子座
日期:2014-12-10 21:39:16处女座
日期:2014-12-02 08:03:17天蝎座
日期:2014-07-21 19:08:47
发表于 2015-06-03 22:34 |显示全部楼层
回复 5# folklore


    如果是这样的话, 加上有问题的变量是本地变量的话, 只能说内存被复用了。 你
自已写个MEMCPY代替memcpy试试, 对MEMCPY(最好在其它编译单元)编译器可能就不敢乱复用了。

论坛徽章:
1
水瓶座
日期:2014-03-20 18:21:14
发表于 2015-06-03 23:18 |显示全部楼层
回复 6# folklore
这也太疯狂了吧……
它凭什么要复用啊我这程序也不大
难道是memcpy在ARM平台上有bug?
等我回去用GDB跟跟看吧,实在不行在X86上编译一个一样的跑跑看

如果说memcpy在ARM上真有此等bug,那我整套所有的程序都将处于危险的境地了,这也太可怕了……

   

论坛徽章:
59
2015年亚洲杯之约旦
日期:2015-01-27 21:27:392015年亚洲杯之日本
日期:2015-02-06 22:09:41拜羊年徽章
日期:2015-03-03 16:15:432015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:50:282015元宵节徽章
日期:2015-03-06 15:50:392015年亚洲杯之阿联酋
日期:2015-03-19 17:39:302015年亚洲杯之中国
日期:2015-03-23 18:52:23巳蛇
日期:2014-12-14 22:44:03双子座
日期:2014-12-10 21:39:16处女座
日期:2014-12-02 08:03:17天蝎座
日期:2014-07-21 19:08:47
发表于 2015-06-04 18:31 |显示全部楼层
回复 7# ecloud


    这有什么疯狂的。 memcpy是标准函数, 编译器知道它要干什么, 你写个MEMCPY,它不知道这个函数干吗的, 不敢优化啊。

论坛徽章:
1
水瓶座
日期:2014-03-20 18:21:14
发表于 2015-06-05 01:41 |显示全部楼层
  1. 55                if (RADEC[0]=='-') dec_sign = -1;
  2. 1: RADEC = "+3240000000642750#\000@"
  3. (gdb)
  4. 56                memcpy(DEC,&RADEC[1],8);
  5. 1: RADEC = "+3240000000642750#\000@"
  6. (gdb)
  7. 57                DEC[8] = 0;
  8. 1: RADEC = "+3240000000642750#\000@"
  9. (gdb)
  10. 58                dec = atoi(DEC);
  11. 1: RADEC = "\000\063\062\064\060\060\060\060\060\060\060\066\064\062\067\065\060#\000@"
复制代码
原来是 DEC[8] = 0;这条语句的问题,DEC字符串在初始化的时候可能地址分配的不对,正好它的[8]是RADEC的[0],就造成了这种结果
可能是ARM上面编译器对内存使用太过吝啬,造成两个字符串紧密相连,因为memcpy只做了8位,致使编译器认为[8]无用,于是一个定义成9个字节的内存空间实际上就给分配了8个字节
我个人认为这是一个相当严重的bug,尤其是当我没有使用O2的时候也这样紧密排列内存空间,基于memcpy来臆断用户定义的变量有多长
解决方法两种
一是定义的时候多一位,定义成DEC[9],就跟后面留出一个缓冲位
二是不用\0去封闭字符串,也就是删掉DEC[8] = 0这句话。这种操作的前提是你的变量数值本身精确对齐,并且不会再对其使用其他的字符串函数(需要\0来判断字符串结束的)
我最后还是采用了第一种方法,虽然我的程序本身数据非常规整毫无例外的可能性,但是也还是给自己留了一个缓冲,封闭了字符串,安全至上!

论坛徽章:
289
射手座
日期:2013-08-23 12:04:38射手座
日期:2013-08-23 16:18:12未羊
日期:2013-08-30 14:33:15水瓶座
日期:2013-09-02 16:44:31摩羯座
日期:2013-09-25 09:33:52双子座
日期:2013-09-26 12:21:10金牛座
日期:2013-10-14 09:08:49申猴
日期:2013-10-16 13:09:43子鼠
日期:2013-10-17 23:23:19射手座
日期:2013-10-18 13:00:27金牛座
日期:2013-10-18 15:47:57午马
日期:2013-10-18 21:43:38
发表于 2015-06-05 08:45 |显示全部楼层
ecloud 发表于 2015-06-05 01:41
原来是 DEC[8] = 0;这条语句的问题,DEC字符串在初始化的时候可能地址分配的不对,正好它的[8]是RADEC的[0] ...


"一是定义的时候多一位,定义成DEC[9]",难道你本来定义的是DEC[8]?那出这个问题很正常啊
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

DTCC2020中国数据库技术大会

【架构革新 高效可控】2020年12月21日-23日第十一届中国数据库技术大会将在北京隆重召开。

大会设置2大主会场,20+技术专场,将邀请超百位行业专家,重点围绕数据架构、AI与大数据、传统企业数据库实践和国产开源数据库等内容展开分享和探讨,为广大数据领域从业人士提供一场年度盛会和交流平台。

http://dtcc.it168.com


大会官网>>
  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP