自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

C/C++返回內(nèi)部靜態(tài)成員的陷阱

開發(fā) 后端
在我們用C/C++開發(fā)的過程中,總是有一個(gè)問題會(huì)給我們帶來苦惱。這個(gè)問題就是函數(shù)內(nèi)和函數(shù)外代碼需要通過一塊內(nèi)存來交互(比如,函數(shù)返回字符串),這個(gè)問題困擾和很多開發(fā)人員。本文則介紹了幾種解決這個(gè)問題的方法。

在我們用C/C++開發(fā)的過程中,總是有一個(gè)問題會(huì)給我們帶來苦惱。這個(gè)問題就是函數(shù)內(nèi)和函數(shù)外代碼需要通過一塊內(nèi)存來交互(比如,函數(shù)返回字符串),這個(gè)問題困擾和很多開發(fā)人員。如果你的內(nèi)存是在函數(shù)內(nèi)棧上分配的,那么這個(gè)內(nèi)存會(huì)隨著函數(shù)的返回而被彈棧釋放,所以,你一定要返回一塊函數(shù)外部還有效的內(nèi)存。

這是一個(gè)讓無數(shù)人困擾的問題。如果你一不小心,你就很有可能在這個(gè)上面犯錯(cuò)誤。當(dāng)然目前有很多解決方法,如果你熟悉一些標(biāo)準(zhǔn)庫的話,你可以看到許多各式各樣的解決方法。大體來說有下面幾種:

1)在函數(shù)內(nèi)部通過malloc或new在堆上分配內(nèi)存,然后把這塊內(nèi)存返回(因?yàn)樵诙焉戏峙涞膬?nèi)存是全局可見的)。這樣帶來的問題就是潛在的內(nèi)存問題。

因?yàn)?,如果返回出去的?nèi)存不釋放,那么就是memory Leak?;蛘呤潜欢啻吾尫牛瑥亩斐沙绦虻腸rash。這兩個(gè)問題都相當(dāng)?shù)膰?yán)重,所以這種設(shè)計(jì)方法并不推薦。(在一些Windows API中,當(dāng)你調(diào)用了一些API后,你必需也要調(diào)用他的某些API來釋放這塊內(nèi)存)

2)讓用戶傳入一塊他自己的內(nèi)存地址,而在函數(shù)中把要返回的內(nèi)存放到這塊內(nèi)存中。這是一個(gè)目前普遍使用的方式。很多Windows API函數(shù)或是標(biāo)準(zhǔn)C函數(shù)都需要你傳入一個(gè)buffer和這個(gè)buffer的長(zhǎng)度。這種方式對(duì)我們來說應(yīng)該是屢見不鮮了。這種方式的好處就是由函數(shù)外部的程序來維護(hù)這塊內(nèi)存,比較簡(jiǎn)顯直觀。但問題就是在使用上稍許有些麻煩。不過這種方式把犯錯(cuò)誤的機(jī)率減到了***。

3)第三種方式顯得比較另類,他利用了static的特性,static的棧內(nèi)存一旦分配,那這塊內(nèi)存不會(huì)隨著函數(shù)的返回而釋放,而且,它是全局可見的(只要你有這塊內(nèi)存的地址)。所以,有一些函數(shù)使用了static的這個(gè)特性,即不用使用堆上的內(nèi)存,也不需要用戶傳入一個(gè)buffer和其長(zhǎng)度。從而,使用得自己的函數(shù)長(zhǎng)得很漂亮,也很容易使用。

這里,我想對(duì)第三個(gè)方法進(jìn)行一些討論。使用static內(nèi)存這個(gè)方法看似不錯(cuò),但是它有讓你想象不到的陷阱。讓我們來用一個(gè)實(shí)際發(fā)生的案例來舉一個(gè)例子吧。

示例

有過socket編程經(jīng)驗(yàn)的人一定知道一個(gè)函數(shù)叫:inet_ntoa,這個(gè)函數(shù)主要的功能是把一個(gè)數(shù)字型的IP地址轉(zhuǎn)成字符串,這個(gè)函數(shù)的定義是這樣的(注意它的返回值):

  1. char *inet_ntoa(struct in_addr in); 

顯然,這個(gè)函數(shù)不會(huì)分配堆上的內(nèi)存,而他又沒有讓你傳一下字符串的buffer進(jìn)入,那么他一定使用“返回static char[]”這種方法。在我們繼續(xù)我們的討論之前,讓我們先了解一下IP地址相關(guān)的知識(shí),下面是inet_ntoa這個(gè)函數(shù)需要傳入的參數(shù):(也許你會(huì)很奇怪,只有一個(gè)member的struct還要放在struct中干什么?這應(yīng)該是為了程序日后的擴(kuò)展性的考慮)

  1. struct in_addr {  
  2. unsigned long int s_addr;  

對(duì)于IPV4來說,一個(gè)IP地址由四個(gè)8位的bit組成,其放在s_addr中,高位在后,這是為了方便網(wǎng)絡(luò)傳輸。如果你得到的一個(gè)s_addr的整型值是:3776385196。那么,打開你的Windows計(jì)算器吧,看看它的二進(jìn)制是什么?讓我們從右到左,8位為一組(如下所示)。

11100001 00010111 00010000 10101100

再把每一組轉(zhuǎn)成十進(jìn)制,于是我們就得到:225 23 16 172, 于是IP地址就是 172.16.23.225。

好了,言歸正傳。我們有這樣一個(gè)程序,想記錄網(wǎng)絡(luò)包的源地址和目地地址,于是,我們有如下的代碼:

  1. struct in_addr src, des;  
  2. ........  
  3. ........  
  4. fprintf(fp, "源IP地址<%s>\t 目的IP地址<%s>\n", inet_ntoa(src), inet_ntoa(des)); 

會(huì)發(fā)生什么樣的結(jié)果呢?你會(huì)發(fā)現(xiàn)記錄到文件中的源IP地址和目的IP地址完全一樣。這是什么問題呢?于是你開始調(diào)試你的程序,你發(fā)現(xiàn)src.s_addr和des.s_addr根本不一樣(如下所示)??蔀槭裁摧敵龅轿募脑春湍康亩际且粯拥??難道說是inet_ntoa的bug?

  1. src.s_addr = 3776385196; //對(duì)應(yīng)于172.16.23.225  
  2. des.s_addr = 1678184620; //對(duì)應(yīng)于172.16.7.100 

原因就是inet_ntoa()“自作聰明”地把內(nèi)部的static char[]返回了,而我們的程序正是踩中了這個(gè)陷阱。讓我們來分析一下fprintf代碼。在我們fprintf時(shí),編譯器先計(jì)算inet_ntoa(des),于是其返回一個(gè)字符串的地址,然后程序再去求inet_ntoa(src)表達(dá)式,又得到一個(gè)字符串的地址。

這兩個(gè)字符串的地址都是inet_ntoa()中那個(gè)static char[],顯然是同一個(gè)地址,而第二次求src的IP時(shí),這個(gè)值的des的IP地址內(nèi)容必將被src的IP覆蓋。所以,這兩個(gè)表達(dá)式的字符串內(nèi)存都是一樣的了,此時(shí),程序會(huì)調(diào)用fprintf把這兩個(gè)字符串(其實(shí)是一個(gè))輸出到文件。所以,得到相同的結(jié)果也就不奇怪。

仔細(xì)看一下inet_ntoa的man,我們可以看到這句話:The string is returned in a statically allocated buffer, which subsequent calls will overwrite. 證實(shí)了我們的分析。

小結(jié)

讓我們大家都捫心自問一下,我們?cè)趯懗绦虻倪^程當(dāng)中是否使用了這種方法?這是一個(gè)比較危險(xiǎn),容易出錯(cuò)的方法。這種陷阱讓人防不勝防。想想,如果你有這樣的程序:

  1. if ( strcmp( inet_ntoa(ip1), inet_ntoa(ip2) )==0 ) {  
  2. .... ....  

本想判斷一下兩個(gè)IP地址是否一樣,卻不料掉入了那個(gè)陷阱——讓這個(gè)條件表達(dá)式永真。

這個(gè)事情告訴我們下面幾個(gè)道理:

1)慎用這種方式的設(shè)計(jì)。返回函數(shù)內(nèi)部的static內(nèi)存有很大的陷阱。

2)如果一定要使用這種方式的話。你就必須嚴(yán)肅地告訴所有使用這個(gè)函數(shù)的人,千萬不要在一個(gè)表達(dá)式中多次使用這個(gè)函數(shù)。而且,還要告訴他們,不copy函數(shù)返回的內(nèi)存的內(nèi)容,而只是保存返回的內(nèi)存地址或是引用是沒用的。不然的話,后果概不負(fù)責(zé)。

3)C/C++是很危險(xiǎn)的世界,如果你不清楚他的話。還是回火星去吧。

【編輯推薦】

  1. malloc/free與new/delete的區(qū)別
  2. 為什么重復(fù)free()比內(nèi)存泄漏危害更大
  3. C++三則 如無必要 勿增虛函數(shù)
  4. 關(guān)于C++虛函數(shù)那點(diǎn)破事
  5. 淺談怎樣加快C++代碼的編譯速度
責(zé)任編輯:于鐵 來源: csdn博客
相關(guān)推薦

2010-01-18 18:04:28

靜態(tài)成員

2010-01-21 14:19:44

C++靜態(tài)成員

2023-10-07 15:53:05

C/C++靜態(tài)變量內(nèi)存

2010-01-18 17:57:02

靜態(tài)數(shù)據(jù)

2024-02-22 18:07:17

C++靜態(tài)成員代碼

2010-01-28 16:42:29

C++靜態(tài)成員

2010-01-21 14:28:03

C++靜態(tài)成員函數(shù)

2010-01-19 18:35:12

靜態(tài)成員

2023-12-04 09:37:00

C++靜態(tài)變量

2010-01-20 17:58:54

C++靜態(tài)成員

2010-02-04 10:08:00

C++靜態(tài)成員函數(shù)

2010-02-04 10:02:08

C++靜態(tài)數(shù)據(jù)成員

2023-03-21 15:21:52

開發(fā)程序設(shè)計(jì)static

2020-07-27 08:05:56

C++語言后端

2010-02-03 11:01:18

C++類靜態(tài)成員初始化

2009-08-28 14:09:19

C#靜態(tài)類

2024-08-26 15:06:20

2025-01-26 16:01:13

C++靜態(tài)成員函數(shù)

2010-02-01 17:31:06

C++類成員

2010-01-27 16:10:32

C++靜態(tài)構(gòu)造函數(shù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)