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);
}
发表于: 2005-01-15 11:35 |
在下新人,也想参与下讨论,哪里说的不对,请各位包涵并指出
因为学校的机器没有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通过.