摘自:www.chinaunix.net R.Ihskaka 新手上路 因为学校的机器没有linux,所以我用了xp+vc6,文件名为test.c
代码是在Release模式下debug的,因为DEBUG模式会对环境有依赖.
这个是源码:
#include<stdio.h> #define SUCCESS 1 #define FAILURE 0 void main() { char *lpTest = "ABCD"; printf("The String is: %s n ",lpTest); *(lpTest+1) = 'X'; printf("The String is: %s n ",lpTest); }
在下新人,也想参与下讨论,哪里说的不对,请各位包涵并指出
因为学校的机器没有linux,所以我用了xp+vc6,文件名为test.c
代码是在Release模式下debug的,因为DEBUG模式会对环境有依赖.
这个是源码:
#include<stdio.h> #define SUCCESS 1 #define FAILURE 0 void main() { char *lpTest = "ABCD"; printf("The String is: %s n ",lpTest); *(lpTest+1) = 'X'; printf("The String is: %s n ",lpTest); }
这是由vC6的Debug的汇编代码,其中带///////// 的为c代码,下面的是对应的汇编代码:
///void main() //这行自己加的,就从这里开始了 { 00401000 55 push ebp 00401001 8B EC mov ebp,esp 00401003 51 push ecx 6: char *lpTest = "ABCD"; //////////////// 00401004 C7 45 FC 30 70 40 00 mov dword ptr [lpTest],offset ___xt_z+8 (00407030) 7: printf("The String is: %s n ",lpTest);//////////////// 0040100B 8B 45 FC mov eax,dword ptr [lpTest] 0040100E 50 push eax 0040100F 68 38 70 40 00 push offset ___xt_z+10h (00407038) 00401014 E8 1F 00 00 00 call _printf (00401038) 00401019 83 C4 08 add esp,8 8: *(lpTest+1) = 'X';//////////////// 0040101C 8B 4D FC mov ecx,dword ptr [lpTest] 0040101F C6 41 01 58 mov byte ptr [ecx+1],58h 9: printf("The String is: %s n ",lpTest);//////////////// 00401023 8B 55 FC mov edx,dword ptr [lpTest] 00401026 52 push edx 00401027 68 50 70 40 00 push offset ___xt_z+28h (00407050) 0040102C E8 07 00 00 00 call _printf (00401038) 00401031 83 C4 08 add esp,8 10: } 00401034 8B E5 mov esp,ebp 00401036 5D pop ebp 00401037 C3 ret
程序运行时,其实lpTest很明显是存放在堆栈里的(0x012ff7c) 从汇编上看到,lpTest开始时,push ecx,并将lpTest指向ecx寄存器的内容为地址的区域,在 mov dword ptr [lpTest],offset ___xt_z+8 (00407030) 后,lpTest才真正指向了定义的"ABCD".而这个"ABCD"的存放地址是0x00407030 . 可见赋值号右边的常量其实是开辟在一个单独的区域里,而且似乎所有的字符常量都是这样.在printf()里的"The String is: %s"也是存放在0x00407030之后的地址上,调用时push的.
这样,对于lpTest本身来说,它其实就是变量而已,可以改变它指向的内容是很正常的.(不正常C语言早浮云了^_^ ) 而对于赋值号右边的字符串常量,它们是单独存放在一个区域的,指针依靠地址来使用他们.
其实只要将 char *lpTest = "ABCD" 改为: const char *lpTest = "ABCD" 就不可以再改变它的数值了.而加上const后的汇编代码,和没有加上时其实是一样的,"ABCD"仍然放在0x00407030. 只是如果你加了const,还试图改变lpTest的数值,那么编译时就会失败,这个看起来是由编译器保证的.这个我不能肯定,编译原理还没学,说不清楚 (>_<)
那么如果是: const char Arry[]="WXYZ" 呢?
00401000 55 push ebp 00401001 8B EC mov ebp,esp 00401003 83 EC 0C sub esp,0Ch 8: const char Arry[]="WXYZ"; //////////////// 00401006 A1 30 70 40 00 mov eax,[___xt_z+8 (00407030)] 0040100B 89 45 F8 mov dword ptr [Arry],eax 0040100E 8A 0D 34 70 40 00 mov cl,byte ptr [___xt_z+0Ch (00407034)] 00401014 88 4D FC mov byte ptr [ebp-4],cl .........................
其实还是一样,字符串常量都是开辟在0x00407030的,但是用了const,就会把这个地址的内容,拷贝到堆栈中去,然后利用 mov cl,byte ptr [___xt_z+0Ch (00407034)] mov byte ptr [ebp-4],cl 来调整一下,使Arry的地址精确指向堆栈中的4个字节,这样就是数组字符串常量了
那再改改,写成这样: const char Arry="WXYZ"; //bt写法,请勿模仿! ^^ 嘿嘿~~编译有个警告,运行,就报错拉~~
00401000 55 push ebp 00401001 8B EC mov ebp,esp 00401003 83 EC 08 sub esp,8 8: const char Arry="WXYZ";///////////////////////// 00401006 B8 30 70 40 00 mov eax,offset ___xt_z+8 (00407030) 0040100B 88 45 FC mov byte ptr [Arry],al ............................
这样写语法是没有错误,但是再执行时,eax中存放的并不是来自0x00407030区域的内容,而是它的地址: mov eax,offset ___xt_z+8 (00407030) 这样再调用printf时,就会出错了,因为 mov byte ptr [Arry],al 会把 0x00000030这样一个数值赋值给Arry,那么printf使用时,会引用这个地址做字符串首地址,那么自然就是非法了.这么低的地址能给你随便访问么~~ 这样的话,你只要改下 printf语句,写成:printf("The String is: %d n ",Arry);或者是printf("The String is: %c n ",Arry); 就可以正常运行了.因为这时候Arry的内容不再做地址,而是做了数值,这样当然没有问题了
呼~呼,高手看了勿笑,菜鸟的一点小见解,有错之处,请斑竹和高手们多多包含 _________________
恩,请教下aero斑竹,如果是 char *lpTest = "ABCD" 的话, 应该怎么设置编译器参数,使得lpTest指向的内容不可修改呢?
常量,今天上来看到这么多常量的讨论...... 其实不管是常量还是变量,必然有地址.包括printf("xxxxx") 这个"xxxxx"照样有地址,vc6下应该在0x00407030开始,往后的某个位置上, (取决于你程序中定义的位置了).只是你定义方式的不同,编译器处理起来就会不同,具体看我发的上个回复.
编译器除了语法检查外,看到的全是地址和数据.const是靠编译器的语法保证的.就象你在C++里取类私有函数的地址,就很困难,是因为编译器在C++编译方式下就不允许,而不是别的什么.
但是就算是const 修饰,也有办法,直接用指针取到地址就行,编译器会给你个类型匹配的警告,不过运行会正常. 用汇编也可以:
#include<stdio.h> #define SUCCESS 1 #define FAILURE 0 void main() { const char Arry[]="WXYZ";
// const char *lpTest = "ABCD";
// printf("The String is: %s n ",lpTest); //*(lpTest+1) = 'X'; printf("The String is: %s n ",Arry); __asm { mov [Arry],31h mov [Arry+1],31h mov [Arry+2],31h mov [Arry+3],31h }
printf("The String is: %s n ",Arry); }
//用int型也是一样
#include<stdio.h> #define SUCCESS 1 #define FAILURE 0 void main() { // const char Arry[]="WXYZ"; const int Arry = 1; // const char *lpTest = "ABCD";
// printf("The String is: %s n ",lpTest); //*(lpTest+1) = 'X'; printf("The Number is: %d n ",Arry); __asm { mov [Arry],99 // mov [Arry+1],31h // mov [Arry+2],31h // mov [Arry+3],31h }
printf("The Number is: %d n ",Arry); }
运行了看结果吧 xp+vc6通过.
|