国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

C++編程易范的錯(cuò)誤

2019-11-17 05:06:15
字體:
供稿:網(wǎng)友
C/C++語言中有許多對(duì)初學(xué)者(甚至是有經(jīng)驗(yàn)的編程人員)來說很輕易范的錯(cuò)誤。通曉這樣的錯(cuò)誤可使你免于陷入其中。
忘記初始化指針
這種錯(cuò)誤只是一般"忘記初始化變量"錯(cuò)誤的一個(gè)非凡形式(C/C++中變量不會(huì)自動(dòng)初始化,而Basic可以)。使這種錯(cuò)誤更糟糕的原因是它的后果往往更加糟糕:
void SomeFunction()
{
int *pnVar
int nVal;
nVal = *pnVar; // Bad enough.
*pnVar = nVal; // MUCh worse.
}
在這個(gè)例子中,指針變量pnVar從未被賦值。因此你必須假設(shè)它含有的是雜亂的數(shù)據(jù),從一個(gè)混亂信息指針中讀數(shù)糟糕的很,因?yàn)榻Y(jié)果肯定是雜亂數(shù)據(jù),向一個(gè)混亂信息指針寫數(shù)據(jù)更糟,因?yàn)樗鼘?dǎo)致一些不知道什么地方的數(shù)據(jù)被重寫。
假如被重寫的區(qū)域無用,這到?jīng)]什么危害。假如被重寫的區(qū)域有用,數(shù)據(jù)就會(huì)丟失。這種類型的錯(cuò)誤那么難找,是因?yàn)橹钡匠绦蚱髨D使用已丟失的數(shù)據(jù)時(shí)問題才會(huì)呈現(xiàn)出來。這種問題可能是在數(shù)據(jù)丟失后好久才發(fā)生的。
由于這一問題手工判定很困難,Visual C++編譯器就通過一些努力來避免它的發(fā)生。例如,當(dāng)你編譯上述函數(shù)時(shí)就會(huì)產(chǎn)生一個(gè)警告。在這種情況下,編譯器會(huì)告訴你變量在使用前未被賦值。在很多情況下,它不可能告訴你。
Windows 95操作系統(tǒng)試圖用保護(hù)存儲(chǔ)器在一定程度上幫助解決難題:假如應(yīng)用程序企圖從不屬于它的存儲(chǔ)器讀或?qū)懀琖indows通常能截獲該請(qǐng)求,并立即終止該程序。可惜,Windows 95不能截獲對(duì)應(yīng)用程序擁有的存儲(chǔ)器的無效訪問,它也不能截獲所有非法訪問,因?yàn)楸仨毐A裟承┤笨冢耘cWindows 3.1的兼容性名義開放。
忘記釋放堆內(nèi)存
請(qǐng)記住從堆獲得分配的任何內(nèi)存都必須要釋放。假如你用完內(nèi)存以后,忘記釋放它,系統(tǒng)內(nèi)存就會(huì)變得愈來愈小,直到最后你的程序不能運(yùn)行而崩潰。
這個(gè)問題會(huì)出現(xiàn)在諸如下列的一些情況中:
Car* GetAnewCar(int nOccupants)
{
Car* pCar;
if(nOccupants < 4)
{
pCar = new Car(2); // get a two-door.
}
else
{
pCar = new Car(4); // otherwise, a four-door.
}
return pCar;
}
void GoToTheStore(int nOccupants)
{
// get a car。
Car* pCar = GetAnewCar(nOccupants);
// Now drive to the store。
if(pCar)
{
pCar->Drive(Store);
}
} 更多文章 更多內(nèi)容請(qǐng)看C/C++技術(shù)專題  java編程開發(fā)手冊(cè)專題,或
在此例中,函數(shù)GoToTheStore()首先分配一輛新車來開——這有點(diǎn)浪費(fèi),但你肯定會(huì)同意這種算法可以正常工作。只要分配了新車,它就會(huì)開到有調(diào)用pCar->Drive(Store)所指向的商店。
問題是在它安全到達(dá)目的地之后,函數(shù)不破壞Car對(duì)象。它只是簡單地退出,從而使內(nèi)存丟失。
通常,當(dāng)對(duì)象pCar出了程序中的作用域時(shí),程序員應(yīng)該依靠析構(gòu)函數(shù)~Car釋放內(nèi)存。但這里辦不到,因?yàn)閜Car的類型不是Car而是Car*,當(dāng)pCar出了作用域時(shí)不會(huì)調(diào)用析構(gòu)函數(shù)。
修正的函數(shù)如下:
void GoToTheStore(int nOccupants)
{
// get a car。
Car* pCar = GetAnewCar(nOccupants);

// Now drive to the store。
if(pCar)
{
pCar->Drive(Store);
}
// Now delete the object,returning the memory.
delete pCar;
}
使用new操作符構(gòu)造的對(duì)象都應(yīng)該用delete運(yùn)算符刪除,這一點(diǎn)必須牢記。
返回對(duì)局部內(nèi)存的引用
另一個(gè)常見的與內(nèi)存有關(guān)的問題是從函數(shù)返回局部內(nèi)存對(duì)象的地址。當(dāng)函數(shù)返回時(shí),對(duì)象不再有效。下一次調(diào)用某函數(shù)時(shí),這個(gè)內(nèi)存地址可能會(huì)被這個(gè)新函數(shù)使用。繼續(xù)使用這個(gè)內(nèi)存指針就有可能會(huì)寫入新函數(shù)的局部內(nèi)存。
這個(gè)常見問題以這種方式出現(xiàn):
Car* GetAnewCar(int nOccupants)
{
Car* pCar;
if(nOccupants < 4)
{
pCar = &Car(2); // get a two-door.
}
else
{
pCar = &Car(4); // otherwise, a four-door.
}
return pCar;
}
請(qǐng)注重指針pCar怎樣被賦予由構(gòu)造函數(shù)Car()建立的未命名對(duì)象的局部地址的。到目前為止,沒有問題。然而一旦函數(shù)返回這個(gè)地址,問題就產(chǎn)生了,因?yàn)樵诜忾]的大括號(hào)處臨時(shí)對(duì)象會(huì)被析構(gòu)。
使運(yùn)算符混亂
C++從它的前輩C那里繼續(xù)了一套含義相當(dāng)混亂模糊的運(yùn)算符。再加上語法規(guī)則的靈活性,就使它很輕易對(duì)程序員造成混亂,使程序員去使用錯(cuò)誤的運(yùn)算符。
這個(gè)情況的最出名的例子如下:
if(nVal = 0)
{
// do something if nVal is nonzero.
} 更多文章 更多內(nèi)容請(qǐng)看C/C++技術(shù)專題  Java編程開發(fā)手冊(cè)專題,或 程序員顯然想要寫if(nVal == 0)。不幸的是,上述語句是完全合法的,雖然沒有什么意義,C++語句將nVal賦值為0,然后檢查結(jié)果看看是否為非零(這是不可能發(fā)生的)。結(jié)果是大括號(hào)內(nèi)的代碼永遠(yuǎn)不會(huì)被執(zhí)行。
其它幾對(duì)輕易弄錯(cuò)的運(yùn)算符是&和&&,以及/和//。
0的四種面孔
根據(jù)使用它的方式,常數(shù)0有四種可能的含義:
☆ 整數(shù)0
☆ 不能是對(duì)象地址的地址
☆ 邏輯FALSE
☆ 字符串的終結(jié)符
我可以向你證實(shí)這些含義的差別是很實(shí)際的。例如,下列賦值是合法的:
int *pInt;
pInt = 0;// this is leagal.
而下列賦值是不合法的:
int *pInt;
pInt = 1;// this is not.
第一個(gè)賦值是合法的,因?yàn)楸碇械牡诙x:常數(shù)0可以是地址,然而常數(shù)1則不行。
這個(gè)含義的多重性能導(dǎo)致一些難以發(fā)現(xiàn)的錯(cuò)誤:
// copy a string from pSource to pTarget -- incorrect version.
while(pSource)
{
*pTarget++ = *pSource++;
}
此例中的while循環(huán)試圖把由pSource指向的源字符串復(fù)制到由pTarget指向的內(nèi)存塊。但不幸的是,條件寫錯(cuò)了,它應(yīng)這樣寫出:
// copy a string from pSource to pTarget -- incorrect version.
while(*pSource)
{
*pTarget++ = *pSource++;
}
你可以看到,當(dāng)由pSource指向的字符為NULL時(shí),終止條件出現(xiàn)。這是0的第四定義。然而,這里寫出的代碼卻是去查看地址pSource是否為零,這是第二定義。
最終結(jié)果是while()循環(huán)繼續(xù)寫入內(nèi)存直到程序崩潰。
0的其他定義之間也可能產(chǎn)生混亂。唯一的解決辦法就是當(dāng)你使用常數(shù)0的時(shí)候小心一點(diǎn)。
聲明的混亂處
復(fù)合聲明是非常混亂的,但C++——以它的熱忱保持了與C的反向兼容性——但也產(chǎn)生了一些聲明間的矛盾,你必須避免這種矛盾。
class Myclass
{
public:
Myclass(int nArg1 = 0,int nArg2 = 0);
};
Myclass mcA(1,2);
Myclass mcB(1);
Myclass mcC();
mcA是參數(shù)1和2構(gòu)成的對(duì)象,而mcB是參數(shù)1和0構(gòu)成的對(duì)象。因此你可能認(rèn)為mcC是參數(shù)0和0構(gòu)成的對(duì)象,然而情況不是這樣。而mcC()是一個(gè)不帶參數(shù)的函數(shù),它用數(shù)值返回類Myclass的對(duì)象。
另一個(gè)混亂產(chǎn)生于初始化運(yùn)算符=的使用:
Myclass mcB = nA; // same as Myclass mcB(nA)

為了增強(qiáng)與C的兼容性,答應(yīng)這樣使用=;然而你應(yīng)該避免這種結(jié)構(gòu),因?yàn)樗皇且回炦m用的。例如下列程序就不會(huì)有預(yù)期的效果:
Myclass mcA = nA,nB;
這說明一個(gè)對(duì)象mcA(nA),它后面有一個(gè)獨(dú)立的使用缺省構(gòu)造符的對(duì)象nB,而不是說明一個(gè)對(duì)象mcA(nA,nB)。
堅(jiān)持使用C++格式——這是最安全的。 更多文章 更多內(nèi)容請(qǐng)看C/C++技術(shù)專題  Java編程開發(fā)手冊(cè)專題,或 計(jì)算順序混亂
C和C++運(yùn)算符的先后順序,使你能夠知道怎樣計(jì)算諸如下列表達(dá)式:
a = b * c + d;
然而先后次序不會(huì)影響子表達(dá)式的計(jì)算順序。讓我們以看上去不重要的方式改變示例的表達(dá)式:
a = b() * c() + d();
現(xiàn)在的問題是,在這個(gè)表達(dá)式中以什么樣的順序調(diào)用函數(shù)b(),c()和d()?答案是,順序是完全不確定的。更糟的是,順序不能借助圓括號(hào)的使用而確定。所以下列表達(dá)式?jīng)]有作用:
a = (b() * c()) + d();
函數(shù)計(jì)算順序通常不值得去關(guān)心。然而,假如這些函數(shù)有副作用,以某種方式彼此影響(稱為相互副作用),那么順序就是重要的了。例如,假如這些函數(shù)改變相同的全局變量,則結(jié)果就是不同的,這取決于其中函數(shù)被調(diào)用的順序。
甚至當(dāng)不涉及函數(shù)調(diào)用時(shí),相互副作用也會(huì)產(chǎn)生影響:
int nI = 0;
cout<<"nA[0]="<這個(gè)表達(dá)式的問題是單個(gè)表達(dá)式包含有相互副作用的兩個(gè)子表達(dá)式——變量nI是增量。哪個(gè)nA[nI++]首先被執(zhí)行,左邊的nA[nI++]還是右邊的nA[nI++]?沒法說,上述代碼可能會(huì)以預(yù)期的方式工作,但也可能不會(huì)。
說明虛擬成員函數(shù)
為了在子類中重載虛擬成員函數(shù),必須用和基本類中函數(shù)一樣的形式說明子類中函數(shù)的參數(shù)和返回類型。這并不總是清楚的。例如,下列代碼似乎講得通:
class Base
{
public:
virtual void AFunc(Base *pB);
};
class Subclass:public Base
{
public:
virtual void AFunc(Subclass *pS);
};
這個(gè)代碼會(huì)編譯通過,但不會(huì)有遲后聯(lián)編。函數(shù)Base::AFunc()的參數(shù)是Base*類型的,而函數(shù)Subclass::AFunc()的參數(shù)是Subclass*,它們是不同的。
這個(gè)規(guī)則的唯一例外是下面的例子,它符合ANSI C++標(biāo)準(zhǔn):
class Base
{
public:
virtual void Base* AFunc();
};
class Subclass:public Base
{
public:
virtual void Subclass* AFunc();
};
在此例中,每個(gè)函數(shù)返回其固有類型對(duì)象的地址。這種技術(shù)很通用,所以標(biāo)準(zhǔn)委員會(huì)決定承認(rèn)它。
從構(gòu)造函數(shù)內(nèi)調(diào)用虛擬成員函數(shù)
從構(gòu)造符內(nèi)調(diào)用虛擬函數(shù)是前期聯(lián)編的,這樣,它就短路掉了那些原本可能的簡潔的能力:
class Base
{
public:
Base();
virtual void BuildSection();
};
class Subclass:public Base
{
public:
Subclass();
virtual void BuildSection();
};
Base::Base()
{
BuildSection();
}; 更多文章 更多內(nèi)容請(qǐng)看C/C++技術(shù)專題  Java編程開發(fā)手冊(cè)專題,或 在此例中,程序員希望構(gòu)造函數(shù)能夠多態(tài)地調(diào)用BuildSection(),當(dāng)正在構(gòu)造的對(duì)象是Base對(duì)象時(shí)調(diào)用Base::BuildSection(),當(dāng)對(duì)象是類Subclass對(duì)象時(shí)調(diào)用Subclass::BuildSection()。

由于下列簡單的原因這個(gè)例子不起作用:當(dāng)調(diào)用BuildSection()完成時(shí),正在構(gòu)造的對(duì)象僅僅是一個(gè)Base對(duì)象。即使對(duì)象最終成為Subclass對(duì)象,也要等到Subclass的構(gòu)造函數(shù)把它過一遍以后。在這些情況下調(diào)用Subclass::BuildSection()可能是致命的。即使對(duì)象將最終成為Subclass對(duì)象,但在調(diào)用BuildSection()的時(shí)候,對(duì)象只不過是Base對(duì)象,而且,這個(gè)調(diào)用必須要前期聯(lián)編到函數(shù)Base::BuildSection()。
指針對(duì)準(zhǔn)
當(dāng)你在80x86處理器(例如,你的PC機(jī)的芯片)上執(zhí)行你的程序時(shí),這個(gè)問題不是致命的,但對(duì)其他的絕大多數(shù)芯片來說,這就是致命的了。它還會(huì)對(duì)你的應(yīng)用程序移植到某個(gè)其他環(huán)境的能力產(chǎn)生影響。此外,甚至對(duì)于Intel 處理器來說,這個(gè)問題也將導(dǎo)致低于標(biāo)準(zhǔn)的性能。
當(dāng)你的指針從一種類型轉(zhuǎn)換到另一種類型的時(shí)候,就有可能產(chǎn)生一個(gè)非對(duì)準(zhǔn)指針(misaligned pointer)。處理器一般要求內(nèi)存塊的地址要與一個(gè)和這個(gè)內(nèi)存塊的尺寸匹配的邊界對(duì)齊。例如,字只能在字邊界上被訪問(地址是二的倍數(shù)),雙字只能在雙字邊界上被訪問(地址是四的倍數(shù)),依次類推。
編譯器通常確保監(jiān)視這個(gè)規(guī)則。但是當(dāng)你的指針類型從一種類型轉(zhuǎn)換成較大類型時(shí),你就可以很輕易地違反這個(gè)規(guī)則:
char cA;
char* pC = &cA;
int* pI;
pI = (int*)pC;
*pI = 0; // this may be fatal.
因?yàn)樽址麅H僅是一個(gè)字節(jié)長,所以地址&cA可能有任意值,包括奇數(shù)值。可是,pI應(yīng)只包含四的倍數(shù)的地址。通過轉(zhuǎn)換,答應(yīng)把pC賦給pI,但是假如地址不是四的倍數(shù),則接著發(fā)生的賦值可能使程序崩潰。
對(duì)于Intel處理器來說,甚至當(dāng)pC值為奇數(shù)時(shí),該賦值也不是致命的;雖然占用的時(shí)間要長得多,但是賦值還是能夠正常執(zhí)行。請(qǐng)你謹(jǐn)防非對(duì)準(zhǔn)指針。
這種情況只在你正在把你的指針從指向一種類型轉(zhuǎn)換成指向較大類型時(shí)才會(huì)出現(xiàn)。[完] 更多文章 更多內(nèi)容請(qǐng)看C/C++技術(shù)專題  Java編程開發(fā)手冊(cè)專題,或

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 汕尾市| 永昌县| 许昌市| 乐安县| 长宁区| 台州市| 虎林市| 双城市| 汶上县| 德令哈市| 磐石市| 延川县| 龙江县| 方山县| 安图县| 安国市| 大宁县| 鄂尔多斯市| 垦利县| 博罗县| 竹北市| 沂源县| 涟源市| 巴彦县| 吉林市| 县级市| 高青县| 苍梧县| 林周县| 嘉荫县| 文安县| 屏东县| 临夏县| 正蓝旗| 库尔勒市| 威信县| 新沂市| 堆龙德庆县| 河曲县| 井研县| 高碑店市|