函式內部與函式外部資料通訊

2021-08-29 09:15:36 字數 2529 閱讀 9842

c/c++返回內部靜態成員的陷阱

陳皓

背景

在我們用c/c ++開發的過程中,總是有乙個問題會給我們帶來苦惱。這個問題就是函式內和函式外**需要通過一塊記憶體來互動(比如,函式返回字串),這個問題困擾和很 多開發人員。如果你的記憶體是在函式內棧上分配的,那麼這個記憶體會隨著函式的返回而被彈棧釋放,所以,你一定要返回一塊函式外部還有效的記憶體。

這是乙個讓無數人困擾的問題。如果你一不小心,你就很有可能在這個上面犯錯誤。當然目前有很多解決方法,如果你熟悉一些標準庫的話,你可以看到許多各式各樣的解決方法。大體來說有下面幾種:

1) 在函式內部通過malloc或new在堆上分配記憶體,然後把這塊記憶體返回(因為在堆上分配的記憶體是全域性可見的)。這樣帶來的問題就是潛在的記憶體問題。因 為,如果返回出去的記憶體不釋放,那麼就是memory leak。或者是被多次釋放,從而造成程式的crash。這兩個問題都相當的嚴重,所以這種設計方法並不推薦。(在一些windows api中,當你呼叫了一些api後,你必需也要呼叫他的某些api來釋放這塊記憶體)

2)讓使用者傳入一塊他自己的記憶體位址,而在函式中把要 返回的記憶體放到這塊記憶體中。這是乙個目前普遍使用的方式。很多windows api函式或是標準c函式都需要你傳入乙個buffer和這個buffer的長度。這種方式對我們來說應該是屢見不鮮了。這種方式的好處就是由函式外部的 程式來維護這塊記憶體,比較簡顯直觀。但問題就是在使用上稍許有些麻煩。不過這種方式把犯錯誤的機率減到了最低。

3)第三種方式顯得比較另 類,他利用了static的特性,static的棧記憶體一旦分配,那這塊記憶體不會隨著函式的返回而釋放,而且,它是全域性可見的(只要你有這塊記憶體的地 址)。所以,有一些函式使用了static的這個特性,即不用使用堆上的記憶體,也不需要使用者傳入乙個buffer和其長度。從而,使用得自己的函式長得很 漂亮,也很容易使用。

這裡,我想對第三個方法進行一些討論。使用static記憶體這個方法看似不錯,但是它有讓你想象不到的陷阱。讓我們來用乙個實際發生的案例來舉乙個例子吧。

示例

char *inet_ntoa(struct in_addr in);

顯然,這個函式不會分配堆上的記憶體,而他又沒有讓你傳一下字串的buffer進入,那 麼他一定使用「返回static char」這種方法。在我們繼續我們的討論之前,讓我們先了解一下ip位址相關的知識,下面是inet_ntoa這個函式需要傳入的引數:(也許你會 很奇怪,只有乙個member的struct還要放在struct中幹什麼?這應該是為了程式日後的擴充套件性的考慮)

struct in_addr

對 於ipv4來說,乙個ip位址由四個8位的bit組成,其放在s_addr中,高位在後,這是為了方便網路傳輸。如果你得到的乙個s_addr的整型值 是:3776385196。那麼,開啟你的windows計算器吧,看看它的二進位制是什麼?讓我們從右到左,8位為一組(如下所示)。

1110000100010111 00010000 10101100

再把每一組轉成十進位制,於是我們就得到:225 23 16 172, 於是ip位址就是 172.16.23.225。

好了,言歸正傳。我們有這樣乙個程式,想記錄網路包的源位址和目地位址,於是,我們有如下的**:

struct in_addr src, des;

........

........

fprintf(fp, "源ip位址<%s>\t 目的ip位址<%s>\n", inet_ntoa(src), inet_ntoa(des));

會 發生什麼樣的結果呢?你會發現記錄到檔案中的源ip位址和目的ip位址完全一樣。這是什麼問題呢?於是你開始除錯你的程式,你發現src.s_addr和 des.s_addr根本不一樣(如下所示)。可為什麼輸出到檔案的源和目的都是一樣的?難道說是inet_ntoa的bug?

src.s_addr = 3776385196; //對應於172.16.23.225

des.s_addr = 1678184620; //對應於172.16.7.100

原 因就是inet_ntoa()「自作聰明」地把內部的static char返回了,而我們的程式正是踩中了這個陷阱。讓我們來分析一下fprintf**。在我們fprintf時,編譯器先計算inet_ntoa (des),於是其返回乙個字串的位址,然後程式再去求inet_ntoa(src)表示式,又得到乙個字串的位址。這兩個字串的位址都是 inet_ntoa()中那個static char,顯然是同乙個位址,而第二次求src的ip時,這個值的des的ip位址內容必將被src的ip覆蓋。所以,這兩個表示式的字串記憶體都是 一樣的了,此時,程式會呼叫fprintf把這兩個字串(其實是乙個)輸出到檔案。所以,得到相同的結果也就不奇怪。

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

小結

Activity資料通訊

若要從子activity獲取返回資訊時,可呼叫以下activity方法 public void startactivityforresult intent intent,int requestcode 該方法的第乙個引數為intent,第二個引數是請求 請求碼是先傳送給子activity,然後再返回...

資料通訊基礎

通道的最高碼元傳輸速率 根據奈氏 nyquist 準則,理想碼元傳輸速率n 2w baud 其中w是理想低通訊道的頻寬,單位為hz,baud是波特,是碼元傳輸速度的單位。通道的極限資訊傳輸速率 shannon用資訊理論的理論推導出了頻寬受限且有高斯白雜訊干擾的通道的極限 無差錯的資訊傳輸速率。其中w...

資料通訊概述

基本概念的介紹 模擬通訊 頻分復用 數字通訊 時分復用 一則離散訊息包含的資訊量 i log ap i log a p i loga p 資料通訊 依照通訊協議,資料傳輸技術在兩個功能單元之間傳遞資料資訊 資料通訊業務 分組交換業務 數字資料業務 幀中繼業務 寬頻業務 資料通訊系統 資料終端裝置dt...