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通過.