[文章] 有關(guān)char指針的文章一篇(轉(zhuǎn)自:http://www.moon-soft.com/doc/9040.htm)
先看以下代碼: char *p; p="abc"; 你認(rèn)為是對(duì)的嗎?
答案:語法上是對(duì)的,但不提倡這種寫法。
誤區(qū)1:沒有給p分配內(nèi)存空間就賦值,怎么會(huì)是對(duì)的呢? 正解:不少人第一眼將這里的p="abc"看成了*p="abc",然后就做出了以上的論斷。這是比較笨笨的錯(cuò)誤咯:) 看清楚就好啦,其實(shí)賦給p的是"abc"的地址。再說,*p="abc"也不對(duì)呀,字符串可不能這么賦值。
誤區(qū)2:這"abcd"哪來的地址,怎么能直接賦給p呢? 正解:先自己試試吧。在2K/XP + VC下運(yùn)行這段代碼,是不會(huì)出錯(cuò)的,說明這段代碼并無問題。暈吧?猜想的話呢,就是"abcd"不知道被放在了什么地方,然后弄來了一個(gè)地址,給了p。
這到底是怎么回事呢?
要知道,這兩個(gè)語句和char *p="abc"是完全一樣的,所以其中的道理也一樣。 char *p="abc"曾經(jīng)迷惑了不少人呀。問問你:p到底是什么類型的?char *?錯(cuò),是const char *! 也就是說,它所指向的內(nèi)容是不可改變的。不過要補(bǔ)充的是,a的指向是可以改變的。 所以為了不再引起誤會(huì),char *p="abc這種寫法是不提倡的。 既然char *p="abcd"被建議寫成const char *p="abcd",那么char *p; p="abcd";也應(yīng)該寫成const char *p; p="abcd";
講來講去,最后來得看看匯編代碼。看完就明白是怎么回事了。(我才發(fā)現(xiàn)匯編代碼原來這么爽看!VC下,沒開編譯器優(yōu)化): 我們重點(diǎn)先看const char *p="abc"和char p[]="abc"有什么不同(都放在main()中聲明):
PHP源碼:
void main()
{
const char *p="abc";
}
3: const char *p="abc";
00401028 mov dword ptr [ebp-4],offset string "abc" (0041f01c)
void main()
{
char p[]="abc";
}
3: char p[]="abc";
00401028 mov eax,[string "abc" (0041f01c)]
0040102D mov dword ptr [ebp-4],eax
看出差別了嗎?上一段ASM用offset取"abc"的地址,然后賦給[ebp-4],也就是p(下同)。 而下一段ASM卻轉(zhuǎn)了一個(gè)彎,先把"abc"的地址轉(zhuǎn)到寄存器eax,然后再轉(zhuǎn)賦給p。
有疑問了:為什么不和上面的一樣,直接用offset?VC是很聰明的(廢話,M$的東西呀),不用offset,恐怕就是用不了了。 offset是一條偽指令,在編譯的時(shí)候就已經(jīng)把偏移量算好了。offset是無法執(zhí)行間接尋址的計(jì)算的。
說明了什么?
const char *p="abc"中的"abc",在編譯期間就已經(jīng)處理好,要了一塊內(nèi)存,存起來了!在把地址賦給p的時(shí)候,就可以直接用offset計(jì)算。 而char p[]="abc"中的"abc",是在運(yùn)行期間動(dòng)態(tài)分配內(nèi)存給"abc",然后再算出地址,賦給p。hehe,這同時(shí)也說明了數(shù)組和指針的等價(jià)性。
我們?cè)僮鲆淮螌?shí)驗(yàn),這一次我們把char p[]="abc"放在main()外,并在main()內(nèi)用一個(gè)指針再指向p看看。
PHP源碼:
char p[]="abc";
void main()
{
char *t=p;
}
5: char *t=p;
00401028 mov dword ptr [ebp-4],offset p (00421adc)
這回p[]的聲明放在main()外,變成了全局變量。結(jié)果main()內(nèi)的t取p的指針的時(shí)候,直接用offset可以計(jì)算出來。
據(jù)小石頭所說,字面值,const,static,inline,全局變量都是放在靜態(tài)數(shù)據(jù)區(qū)的。(但我感覺似乎不是如此,只是全局變量和字符串在編譯時(shí)就處理好放在一起)
不難發(fā)現(xiàn),p[]被聲明成全局變量后,就可以直接用offset計(jì)算地址,說明靜態(tài)數(shù)據(jù)區(qū)是編譯時(shí)就已經(jīng)處理好的。 再對(duì)照const char *p="abc"的ASM,我們馬上想到:"abc"就是被C/C++存在靜態(tài)數(shù)據(jù)區(qū)中的!它的地址就是"abc"在靜態(tài)數(shù)據(jù)區(qū)的地址!
弄清了這個(gè),有些問題也就可以想得通了。下面這種用法,看來不能說是錯(cuò)誤的了,因?yàn)?abc"是在靜態(tài)數(shù)據(jù)區(qū)的,生存期可以說是整個(gè)程序:
PHP源碼:
#include "stdio.h"
const char *fun()
{
const char *p="abc";
return p;
}
void main()
{
const char *t=fun();
printf ("%s",t);
}
看ASM:
PHP源碼:
5: const char *p="abc";
00401038 mov dword ptr [ebp-4],offset string "%d" (0042001c)
一樣也是使用offset計(jì)算地址。
C/C++給字符串的待遇真是太好了。為了一個(gè)字符串,幾乎可以打破所有的指針規(guī)則。暈~~~~(完)
附:第一次寫這么長(zhǎng)的文章,寫得挺暈的。本來我的C/C++也不是很純熟的,ASM也是一知半解,今天在CSDN上為這個(gè)問題郁悶了半天,和幾個(gè)人討論了一下,最后就寫了這么一篇文章。希望大家賞臉看看~~~有錯(cuò)一定要指正啊!最后特別感謝小石頭(想飛的菜鳥,驕傲的石頭,菜菜,都是他咯),還有小阿哥(就是Kingzeus咯)的幫忙~~~~~~謝謝咯~~~~~~
__________________ 小菜虎 -> 菜菜的老虎
驕傲的石頭回復(fù):
堆幾盤積木,心情好些了,所以再重新寫一遍。 關(guān)于字符串的這個(gè)問題,我一直在心里困惑著。所以呢,昨天就看了一下。 以前回答別人的時(shí)候,總是很簡(jiǎn)單的回答,字符串就是const char *指針,指向它的入口地址。現(xiàn)在想來真是慚愧,雖然這個(gè)事實(shí)好象已經(jīng)為大家所接受,甚至沒有人探討過這個(gè)問題!所以我相信我的發(fā)現(xiàn)對(duì)大家大多數(shù)是有好處的。 首先請(qǐng)看以下代碼
PHP源碼:
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
const char *p = 0;
char *p2 = p;
return 0;
}
以上代碼有問題嗎? 如果你說沒有,請(qǐng)你試一下。很明顯,這是有問題的。const是為了保證不變性,而你把他變成non const,肯定有錯(cuò)誤或者警告,要么就要用const_cast轉(zhuǎn)換。 所以上面的代碼不能通過編譯。
那么這就很明顯的在lee的post里出現(xiàn)了問題,當(dāng)然我以前也一直是這么認(rèn)為,甚至很多人都是這么認(rèn)為。難道這是編譯器對(duì)字符串的特殊處理嗎? 還是其他的原因?
于是我想看看究竟。就動(dòng)用了RTTI,我飛快的鍵入了以下代碼。
PHP源碼:
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
cout << typeid("abc").name() << endl;
return 0;
}
你說結(jié)果是什么? 是char [4]!而不是const char *; 好,這個(gè)結(jié)果解決了我心中的疑點(diǎn),原來是這樣!這可以很簡(jiǎn)單的解釋char *p = "abc"這個(gè)問題。 數(shù)組是一個(gè)char *const 指針,當(dāng)然可以賦給char *指針而不會(huì)影響其常量性。所以這是完全正確的賦值。 其實(shí)這想起來也很平常,指針是沒有分配空間的地址而已,而數(shù)組是一種容器,占用連續(xù)的儲(chǔ)存空間。想想字符串就該知道它是一個(gè)數(shù)組!而不是指針!真正意義上的指針只能是地址,而它在分配了連續(xù)的空間后可以作為數(shù)組來使用,這是由于他們的共性而決定的。
哈哈!心情愉快,所以也接著看了下lee上面所做的探討。字符串是放在靜態(tài)儲(chǔ)存區(qū)沒錯(cuò),毫無疑問。至于位置~ 偶不想多說,lee在上面分析了很多。所以我只對(duì)它進(jìn)行了一下簡(jiǎn)單的測(cè)試。 我手頭上只有dev c++ 和 vc70編譯器,所以就只用他們進(jìn)行了測(cè)試。(打開了全部?jī)?yōu)化)
PHP源碼:
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
char *p = "abc"
char *p2 = "abc";
p[1] = 'k';
p2[1] = 'j'
cout << p1 << endl;
cout << p2 << endl;
return 0;
}
結(jié)果呢?在dev c++下報(bào)錯(cuò), 原因我想可能是因?yàn)樵赿ev c++在對(duì)靜態(tài)儲(chǔ)存區(qū)進(jìn)行了保護(hù)。而vc70下,通過,并且兩個(gè)輸出是不同的。所以偶去看vc70產(chǎn)生的msil碼,原來vc70每次處理字符串都在靜態(tài)區(qū)分配了空間,而且這兩個(gè)"abc"是連續(xù)分配的!這里就沒有出現(xiàn)Solmyr說的那種情況。我想一般比較好的編譯器也應(yīng)該這么說。至于vc60,偶沒有測(cè)試,但是可能Solmyr說的情況會(huì)出現(xiàn)吧。但這樣是不好的,相信這樣會(huì)出現(xiàn)很多微妙的情況。 所以,引用字符串的最佳格式是const char *指針,但char *指針是完全沒錯(cuò)的。于情于理,也說的過去吧 。
如果你還要問,那用指針接受數(shù)組呢? 呵呵,你該仔細(xì)看看前面了。相信這會(huì)給你幫助。
__________________ 不可一日無酒無肉無女人
 |