| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 1057 人关注过本帖
标题:很诡异的代码,期待高手
只看楼主 加入收藏
sunkaidong
Rank: 4
来 自:南京师范大学
等 级:贵宾
威 望:12
帖 子:4496
专家分:141
注 册:2006-12-28
收藏
得分:0 
晕,为什么我发的帖子都是两个呢?

学习需要安静。。海盗要重新来过。。
2009-09-04 18:33
UserYuH
Rank: 12Rank: 12Rank: 12
来 自:毅华
等 级:火箭侠
威 望:8
帖 子:720
专家分:3300
注 册:2009-8-10
收藏
得分:0 
memset(&te,0,strlen(te)); 用法不对,放数组te,放&te会把数组的地址给清了,te不在指向任何地址,甚至把邻近的地址也清0,由于各机子申请内存空间地址不同,结果也不同。
正确用法:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
main()
{
    char t[]="werwer";
    char *tt;
    tt=t;
    char te[7]="sdfsd";
    memset(te,'0',strlen(te));  
    printf("%s %s\n",te,tt);
}


[ 本帖最后由 UserYuH 于 2009-9-4 18:44 编辑 ]

努力—前进—变老—退休—入土
2009-09-04 18:41
prankmoon
Rank: 8Rank: 8
等 级:蝙蝠侠
帖 子:161
专家分:921
注 册:2009-7-21
收藏
得分:6 
因为存储tt地址的内存被memset函数置为了0,对于指针来说也就是NULL了。
                                                                         .
我们用如下代码进行测试并对其进行解释,VC6 + Debug方式:
程序代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
    char t[]="werwer";
    char *tt = t;
    char te[7];
   
    memset(te,0,strlen(te));  //取消注释tt为null
    printf("%s\n",tt);
    return 0;
}

我们在memset行下断点,看看memset函数执行前的内存:
0012FF6C  CC CC CC CC CC CC CC CC  烫烫烫烫
0012FF74  78 FF 12 00 77 65 72 77  x...werw
0012FF7C  65 72 00 CC C0 FF 12 00  er.汤...
                                                                         .
我们对这段数据进行解释:
1. 0012FF6C是字符数组te指向的字符串。显然,因为该数组没有被初始化,所以,其指向的是内存中的无效数据,这里被设置为连续8字节的CC;
                                                                         .
2. 0012FF74是存储变量tt自身的地址,tt的值是0x0012FF78(这4字节顺序倒着看),也就是字符数组t所指向的字符串的地址;
                                                                         .
3. 0012FF78是字符数组t所指向的字符串,该字符串以\0结尾(也就是说到0012FF7E该字符串就结束了)。
                                                                         .
然后我们执行memset函数,并查看刚才的那段内存:
0012FF6C  00 00 00 00 00 00 00 00  ........
0012FF74  00 00 00 00 77 65 72 77  ....werw
0012FF7C  65 72 00 CC C0 FF 12 00  er.汤...
                                                                         .
现在我们重新解释这段内存数据:
1. 因为strlen(te)在计算时必须遇到\0才算结束,也就是说,直到0012FF77才碰到了\0,这样memset(te, 0, strlen(te))就把从0012FF6C~0012FF77全部置为0了。注意到,指针tt(0012FF74)原先的值0x0012FF78也被覆盖为0了,其道理和赋值语句 tt = NULL;类似,只不过是被覆盖,而不是显式赋值而已。
                                                                         .
2. 所以,因为tt等于NULL(当然也就是0)了,因此,printf函数打印的就是NULL了。
                                                                         .
这段代码也从另一方面说明了字符串指针和字符串数组是有差异的。我们可以进一步做如下测试:
程序代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
    char t[]="werwer";
    char *tt = t;
    printf("%08X %08X\n", &t, *&t);
    printf("%08X %08X\n", &tt, *&tt);
    printf("%s\n", t);
    printf("%s\n", &t);
    return 0;
}

输出的结果:
0012FF78 0012FF78
0012FF74 0012FF78
werwer
werwer
                                                                         .
我们看到,&t和*&t的值居然相同,而&tt和*&tt的值却不相同;t和&t打印出了相同的字符串是因为&t的值就等于t的值(0012FF78)。所以LZ的memset(&te, 0, strlen(te))和memset(te, 0, strlen(te))完全相同,也不存在语法错误。但是提倡用te,而不是&te,这样,别人更容易看明白这个memset函数的意思。
                                                                         .
说了这么长一段话,希望能够对楼主有所帮助。

[ 本帖最后由 prankmoon 于 2009-9-4 23:38 编辑 ]
2009-09-04 23:37
UserYuH
Rank: 12Rank: 12Rank: 12
来 自:毅华
等 级:火箭侠
威 望:8
帖 子:720
专家分:3300
注 册:2009-8-10
收藏
得分:6 
楼上说得不错,参数&te和te是一样,但我有个更直观的测试方法,如下代码:
程序代码:
main()
{
  char a[]="kkkk",b[]="cccc",*p;
  int i;
  p=a;
  printf("a:%x~%x  b:%x~%x\n",a,a+4,b,b+4);
  for(i=0;i<=10;i++)
    printf("%x=%c ",p+i,*(p+i));
  printf("\n");
  memset(a,'A',11);
  printf("a:%x~%x  b:%x~%x\n",a,a+4,b,b+4);
  for(i=0;i<=10;i++)
    printf("%x=%c ",p+i,*(p+i));
  printf("\n\n");
    getch();
}
运行结果:
a:ffd0~ffd4  b:ffd6~ffda
ffd0=k ffd1=k ffd2=k ffd3=k ffd4=  ffd5= ffd6=c ffd7=c ffd8=c ffd9=c ffda=
a:ffd0~ffd4  b:ffd6~ffda
ffd0=A ffd1=A ffd2=A ffd3=A ffd4=A ffd5=A ffd6=A ffd7=A ffd8=A ffd9=A ffda=A
·
a数组地址:ffd0~ffd4  b数组地址:ffd6~ffda
以a为首地址:ffd0~ffda的值运行memset(a,'A',11);后,ffd0~ffda的值全都赋值'A',a和b两数组的值全变。
上面可以看出用memset函数注意第三个参数,使用得不当会导至严重错误。

[ 本帖最后由 UserYuH 于 2009-9-5 01:18 编辑 ]

努力—前进—变老—退休—入土
2009-09-05 01:17
prankmoon
Rank: 8Rank: 8
等 级:蝙蝠侠
帖 子:161
专家分:921
注 册:2009-7-21
收藏
得分:0 
ls,你给你上面代码中最后的getch();和其上的printf("\n\n");之间加上printf("%s\n", b);你看看b会不会变。我在VC6 + Debug方式下,b还是原来的"cccc"。如下:
程序代码:
....
{
....
printf("\n\n");
printf("%s\n", b);
getch();
}

你的代码(加上我添的那一行)在我这里运行的结果:
a:12ff78~12ff7c  b:12ff70~12ff74
12ff78=k 12ff79=k 12ff7a=k 12ff7b=k 12ff7c= 12ff7d=?12ff7e=?12ff7f=?12ff80=?12ff81= 12ff82=X
a:12ff78~12ff7c  b:12ff70~12ff74
12ff78=A 12ff79=A 12ff7a=A 12ff7b=A 12ff7c=A 12ff7d=A 12ff7e=A 12ff7f=A 12ff80=A 12ff81=A 12ff82=A
                                                             .
cccc
注:出现了一个不可打印的字符,所以无法在论坛贴出来,因此这里用X代表那个字符(值是0x12)。
                                                             .
这个主要牵涉到函数内部的局部变量在内存中的排列顺序问题,是tc和VC6(抑或其它的>=32位的编译器?)在这个问题上的处理方式不同,还是16位的实模式与保护模式之间的差异?还是其他所致,这个我也说不清楚。
                                                             .
上面的代码在我这里运行结束时还出现了异常:
---------------------------
Microsoft Visual C++
---------------------------
Unhandled exception in chess.exe: 0xC0000005: Access Violation.
---------------------------
确定
---------------------------
是因为函数返回地址被覆盖为AAA\0,从而导致了这个异常。
                                                             .
btw:因为在VC6下查看内存很方便,所以我个人更喜欢逐步跟踪内存的变化情况,而不是使用printf打印。附件是我调试时的抓屏。
vc6_debug.zip (32.77 KB)


[ 本帖最后由 prankmoon 于 2009-9-5 02:22 编辑 ]
2009-09-05 01:55
UserYuH
Rank: 12Rank: 12Rank: 12
来 自:毅华
等 级:火箭侠
威 望:8
帖 子:720
专家分:3300
注 册:2009-8-10
收藏
得分:0 
回复 17楼 prankmoon
a:12ff78~12ff7c  b:12ff70~12ff74
你注意看你的a和b数组地址。数组b地址:12ff70在数组a:12ff78前,程序是按数组a:12ff78后的11个字节赋'A'值,数组b地址里的值没动到。输出b当然还是原来的值了。
·
你给的结果和我的结果说明:不同机子,不同编译器给变量申请的内存地址的不同,或不在同一地地段,或同在一地址段的先后顺序不同。结果也不一样,但程序运行的所做的动作是一样的,都是把以数组a地址为首的后11字节赋'A'值。

努力—前进—变老—退休—入土
2009-09-05 08:54
hackzbst
Rank: 2
等 级:论坛游民
帖 子:27
专家分:54
注 册:2009-6-10
收藏
得分:0 
现在才结贴,希望各位大大别敲我啊,放周末了在~~~
学到东西了,嘿嘿~~

别迷恋哥,哥只是一个传说......
2009-09-07 14:52
hackzbst
Rank: 2
等 级:论坛游民
帖 子:27
专家分:54
注 册:2009-6-10
收藏
得分:0 
回复 14楼 UserYuH
非常感谢老大,看懂了~~

别迷恋哥,哥只是一个传说......
2009-09-07 14:56
快速回复:很诡异的代码,期待高手
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.018654 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved