云風(fēng)的 BLOG: 一個(gè) C 接口設(shè)計(jì)的問(wèn)題
一個(gè) C 接口設(shè)計(jì)的問(wèn)題
C 語(yǔ)言在本質(zhì)上,參數(shù)傳遞都是值傳遞。不像 Pascal 和 C++ 可以傳引用。這一點(diǎn),使得 C 語(yǔ)言可以保持簡(jiǎn)單的設(shè)計(jì),但另一方面也頗為人詬病。
因?yàn)樾阅軉?wèn)題,指針不得不被引入??梢哉f(shuō),用 C 語(yǔ)言實(shí)現(xiàn)的軟件,其實(shí)現(xiàn)的 Bug 90% 以上都來(lái)至于指針,應(yīng)該是沒(méi)有夸大了。當(dāng)然設(shè)計(jì)引起的問(wèn)題或許更為關(guān)鍵一些,那些于指針無(wú)關(guān)。
糾結(jié)于性能問(wèn)題上,層次比較低。可 C 語(yǔ)言就是一個(gè)活躍在較低層次的語(yǔ)言,一旦你選擇用它,就不得不關(guān)心性能問(wèn)題。反過(guò)來(lái),把 C 模仿成更高級(jí)的語(yǔ)言,倒是有點(diǎn)畫(huà)蛇添足了。好了,讓我們來(lái)看個(gè)實(shí)際的涉及參數(shù)傳遞的相關(guān)問(wèn)題,用 C 語(yǔ)言該如何設(shè)計(jì)。
最近同事在做一個(gè)類(lèi)似 Protocol Buffers 的東西。這個(gè)東西做好并不容易,設(shè)計(jì)上尤為困難。其中的設(shè)計(jì)難點(diǎn):設(shè)計(jì)一個(gè)合適的 DSL (領(lǐng)域?qū)S谜Z(yǔ)言) 我們討論過(guò)很久,也分析了好幾天,但今天不打算談了。揀個(gè)小東西說(shuō):當(dāng)我們把一個(gè)二進(jìn)制結(jié)構(gòu)化數(shù)據(jù)塊解析出來(lái),傳遞到 C 語(yǔ)言中,讓 C 語(yǔ)言可以方便的訪(fǎng)問(wèn)數(shù)據(jù)結(jié)構(gòu)時(shí),接口如何設(shè)計(jì)?
?
這個(gè)問(wèn)題在目標(biāo)語(yǔ)言不是 C 而是更高級(jí)的語(yǔ)言(尤其是有 gc 機(jī)制的語(yǔ)言)時(shí),都不是問(wèn)題???C 語(yǔ)言本身是沒(méi)有對(duì)象概念的。
C 語(yǔ)言有結(jié)構(gòu),但是不具備描述動(dòng)態(tài)長(zhǎng)度的能力;沒(méi)有字符串,只有定長(zhǎng)的字符數(shù)組;甚至沒(méi)有多維數(shù)組,只有一維數(shù)組的數(shù)組。
C 函數(shù)的參數(shù)及返回值可以是結(jié)構(gòu),但在接口設(shè)計(jì)中,或許是因?yàn)橹祩鬟f,以及考慮 ABI 的簡(jiǎn)潔性的關(guān)系,常常使用結(jié)構(gòu)指針。返回結(jié)構(gòu)指針往往有生命期管理的苦惱。即使到了 C++ 里,允許返回結(jié)果/對(duì)象了,可所謂返回值優(yōu)化也是件相當(dāng)讓人困擾的事情(如果你打算完全放棄了解語(yǔ)言的細(xì)節(jié),無(wú)視細(xì)微處的性能問(wèn)題。那么,為什么不考慮使用 Java 或是 Python ,無(wú)論什么都比選擇 C++ 強(qiáng))。
對(duì)于返回一組復(fù)雜數(shù)據(jù),通常的辦法有些什么?
最常用的方法是,調(diào)用者分配空間,傳遞給處理函數(shù)。由處理函數(shù)反向填寫(xiě)結(jié)構(gòu)內(nèi)容。這樣的好處是,調(diào)用者可以選擇把空間分配在棧上還是堆上。一點(diǎn)小提示:在語(yǔ)法上,C 語(yǔ)言允許你把一個(gè)數(shù)組當(dāng)成指針來(lái)傳遞。所以你可以定義一個(gè)長(zhǎng)度為 1 的結(jié)構(gòu)數(shù)組類(lèi)型。用起來(lái)好看一些。具體見(jiàn) 標(biāo)準(zhǔn)庫(kù)中的 setjmp 的定義 。不過(guò)作為我個(gè)人的理念來(lái)說(shuō),不太主張?jiān)?C 語(yǔ)言設(shè)計(jì)的軟件中,為了減少幾次鍵盤(pán)輸入,而使用過(guò)多的語(yǔ)言特性。
這個(gè)方式的缺點(diǎn)是,你很難讓調(diào)用者定義不定的數(shù)據(jù)結(jié)構(gòu)。尤其是在結(jié)構(gòu)里還有對(duì)別的結(jié)構(gòu)的引用。
跟這個(gè)相似的是接收字符串。最典型的例子是標(biāo)準(zhǔn)庫(kù)中的 fgets ,提供一個(gè)接收緩沖區(qū)的地址指針,和一個(gè)緩沖區(qū)大小。(注:gets 則是一個(gè)失敗的設(shè)計(jì))同樣在 Windows 的 API 中,也隨處可見(jiàn)這樣的例子。
第二,就是由函數(shù)自己分配內(nèi)存,交給調(diào)用者去釋放。大家只需要約定內(nèi)存管理的接口即可。標(biāo)準(zhǔn)庫(kù)中的 strdup 就是這樣做的,同樣的還有 readline 庫(kù)中的 readline 。C 語(yǔ)言統(tǒng)一使用 malloc 管理內(nèi)存,不像 C++ 提供了更靈活(更難控制,更容易出問(wèn)題?)的 new 操作符重載。所以,給出這個(gè)約定并不會(huì)增添太多的麻煩。btw, 由于微軟 VC 的 CRT 對(duì) malloc 等實(shí)現(xiàn)的過(guò)于糟糕,導(dǎo)致很多 Windows 的軟件自行實(shí)現(xiàn)內(nèi)存管理器。或者在庫(kù)中開(kāi)放自定義內(nèi)存管理器注入的接口。這其實(shí)有點(diǎn)越俎代庖了。gcc 提供的 CRT 里, malloc 性能就相當(dāng)不錯(cuò)了。
缺點(diǎn)呢??jī)?nèi)存只能從堆上分配;而且增加了內(nèi)存泄露的隱患;設(shè)計(jì)角度上講,也不太干凈。對(duì)于復(fù)雜數(shù)據(jù)結(jié)構(gòu),這個(gè)方法也無(wú)能為力。C 語(yǔ)言里并沒(méi)有所謂析構(gòu)函數(shù)的說(shuō)法。
作為對(duì)第二點(diǎn)的一種補(bǔ)充方案,用的人就鳳毛麟角了。那就是給你的系統(tǒng)加入 gc 。實(shí)際上,就是約定另一種內(nèi)存管理方法。我們的項(xiàng)目部分模塊在用,效果還不錯(cuò)。 gc 庫(kù)已經(jīng)開(kāi)源,請(qǐng)參考這里 。如果信不過(guò)這套東西,可以考慮 COM 的機(jī)制:增減引用。COM 旨在建立一種對(duì)象模型,可惜 C 語(yǔ)言中沒(méi)有對(duì)象的概念,在 C 的層面使用 COM ,痛苦了一些。對(duì)于粒度比較小的東西,性能也將是問(wèn)題。
第三種,用的人也比較多。就是在函數(shù)內(nèi)部開(kāi)一塊靜態(tài)空間,用于數(shù)據(jù)返回。返回的指針指向的數(shù)據(jù)的生命期可以保證到下次調(diào)用同一函數(shù)之前。靜態(tài)空間可以聲明在數(shù)據(jù)段里,也可以在程序初始化時(shí)從堆上分配出來(lái),這樣利于在空間不夠的時(shí)候擴(kuò)展。至于這塊靜態(tài)空間什么時(shí)候釋放的問(wèn)題,不用太操心。即使不去釋放它們也不用內(nèi)疚。操作系統(tǒng)會(huì)幫你回收的,還會(huì)比你干的更出色。C 是為了實(shí)現(xiàn) UNIX 而誕生,而 UNIX 的哲學(xué)就是,編寫(xiě)簡(jiǎn)單的程序?qū)P母珊米约旱氖拢尭邔哟蔚某绦颍ㄍǔJ?shell 或動(dòng)態(tài)語(yǔ)言)去組合它們,讓操作系統(tǒng)去管理它們。在 Windows 上,Unix 編程哲學(xué)未必有用,但大原則沒(méi)錯(cuò)的。
這個(gè)方案有另外一個(gè)問(wèn)題,就是函數(shù)不可重入,且有線(xiàn)程安全問(wèn)題。重入問(wèn)題可以想辦法避免。線(xiàn)程安全可以用 TLS 解決。老實(shí)說(shuō),我個(gè)人不看好在 C 語(yǔ)言中使用多線(xiàn)程解決問(wèn)題。多線(xiàn)程也是違背 Unix 哲學(xué)的。如果你有幾件事情需要協(xié)調(diào)起來(lái)做,使用多進(jìn)程;如果你有幾百件事情需要同時(shí)來(lái)做,考慮換個(gè)思路,玩玩 Erlang 啥的。
?
?
回到今天我們面臨的問(wèn)題。用一種 DSL 來(lái)描述一個(gè)數(shù)據(jù)結(jié)構(gòu)(比 C 的結(jié)構(gòu)表達(dá)能力更強(qiáng)的),然后生成對(duì)應(yīng)語(yǔ)言的解析庫(kù)。如果目標(biāo)語(yǔ)言是 C 的話(huà),我們生成的代碼如何返回對(duì) C 程序員友好的結(jié)構(gòu)化數(shù)據(jù)呢?
這讓我想到了 MySQL 的 C 語(yǔ)言接口。很多初學(xué) C++ 的程序員,很喜歡把那些 C 接口“封裝”成“漂亮”的 C++ 接口。直接返回 vector 套 map 的多層模板實(shí)例。不知道有多少人干過(guò)?前幾年我?guī)?shí)習(xí)生的時(shí)候反正見(jiàn)過(guò)不少。如果同學(xué)你現(xiàn)在醒悟了,明白這是件巨傻X 的事情,那么握握手,我們有共同理念;否則(C++ 封裝以后不是很“酷”嗎?),我們暫時(shí)沒(méi)有共同語(yǔ)言了。
我不是想說(shuō) MySQL 的 C 接口設(shè)計(jì)的很好,不過(guò)是中規(guī)中矩。只是 C++ 不是 C ,C 也不是 C++ 。(話(huà)說(shuō),上面提到的 C++ 封裝,我也不認(rèn)為是正確的使用 C++ )反復(fù)提及 C++ ,是因?yàn)?,我發(fā)現(xiàn)今天很大比例的 C 程序員其實(shí)是從 C++ 開(kāi)始啟蒙的,而不是相反。把 C++ 當(dāng)成 C 用的危害其實(shí)比不上把 C 當(dāng)成 C++ 用。前者不過(guò)是把汽車(chē)開(kāi)到自行車(chē)的速度,至少不怕摔跤了,跑起來(lái)還能安全點(diǎn);而后者,非要把自行車(chē)踩到高速公路中間,遲早非撞死不可。
最方便 C 程序使用的莫過(guò)于傳入一個(gè)結(jié)構(gòu)指針,讓庫(kù)去解析數(shù)據(jù),填寫(xiě)這個(gè)結(jié)構(gòu)了。
但是,如果結(jié)構(gòu)里有字符串、不定長(zhǎng)數(shù)組(通常會(huì)根據(jù)前面解析出來(lái)的數(shù)據(jù)決定后面的長(zhǎng)度,對(duì)于 C 的編程技巧來(lái)說(shuō),允許把結(jié)構(gòu)體的最后一個(gè)數(shù)組的長(zhǎng)度設(shè)為 0 ,假設(shè)成不定長(zhǎng)的,從而減少一次間接的指針引用。但是對(duì)于結(jié)構(gòu)中有多個(gè)不定長(zhǎng)數(shù)組則無(wú)法使用這個(gè)技巧。)等等的話(huà),就很難避免指針了。
數(shù)據(jù)中一旦出現(xiàn)指針(間接引用別的數(shù)據(jù)),就有內(nèi)存管理問(wèn)題。
最開(kāi)始,考慮過(guò)一個(gè)很 C++ 的方案,傳入一個(gè)內(nèi)存管理器。這種設(shè)計(jì)在 STL 里就有。所有 STL 的容器都可以指定一個(gè) allocator ,供靈活的管理內(nèi)存。前幾年我倒是認(rèn)為這是個(gè)相當(dāng)巧妙的東西。沒(méi)有細(xì)想,自定義分配器最終有多大的意義?自定義內(nèi)存管理器,很大程度上是因?yàn)樾室蛩匾鸬?。但性能?wèn)題永遠(yuǎn)不是根本問(wèn)題。制作軟件是為了達(dá)到特定的目的,而軟件開(kāi)發(fā)的問(wèn)題更多的是是解決復(fù)雜度問(wèn)題。往往復(fù)雜度帶來(lái)的性能問(wèn)題更加嚴(yán)重。然后為了解決復(fù)雜度帶來(lái)的性能問(wèn)題去引入更高的復(fù)雜度,出現(xiàn)惡性循環(huán)的可能性非常之大。
即使我們傳入的內(nèi)存管理器(或是直接使用 CRT 里的 malloc,但這樣就沒(méi)可能利用堆棧分配空間了),還會(huì)面臨新問(wèn)題,如何回收結(jié)構(gòu)中間接引用的數(shù)據(jù)。引入析構(gòu)函數(shù)指針?OMG 。
后來(lái),我們?cè)O(shè)想使用一個(gè)內(nèi)部靜態(tài)空間,所有的解析結(jié)果都分配在內(nèi)部,自我管理。這些空間還可以復(fù)用。大部分解析結(jié)果也就是臨時(shí)用用,這樣做很方便。而且調(diào)用者不用太關(guān)心數(shù)據(jù)的生命期。
但是,一旦調(diào)用者需要把結(jié)果(一個(gè)復(fù)雜結(jié)構(gòu))保存一段時(shí)間的話(huà),他就遇到困難。
當(dāng)然,也可能不是困難。當(dāng)我們面對(duì)這個(gè)設(shè)計(jì)難點(diǎn)時(shí),都應(yīng)該向上考慮一層,究竟這是一個(gè)問(wèn)題嗎?我們需要這么用嗎?
調(diào)用者可以自己遍歷這個(gè)數(shù)據(jù)結(jié)構(gòu),把他需要的數(shù)據(jù),以自己的方式復(fù)制出來(lái),組織起來(lái)。他們需要的是數(shù)據(jù),而不是對(duì)數(shù)據(jù)結(jié)構(gòu)完全的拷貝。
仔細(xì)考慮過(guò)以后,我們還是發(fā)現(xiàn),保留完整的數(shù)據(jù)結(jié)構(gòu)是有意義的。不像 C++ ,C 沒(méi)有對(duì)象賦值操作符重載這種語(yǔ)法糖,我也不喜歡用宏去模擬一個(gè)出來(lái)。增加一個(gè)拷貝函數(shù)指針其實(shí)和增添一個(gè)析構(gòu)函數(shù)一樣,對(duì) C 來(lái)說(shuō),不那么漂亮。(當(dāng)然,同時(shí)增加了開(kāi)發(fā)量,我們需要編寫(xiě)更多的代碼自動(dòng)生成器)
最終,我們采用了由調(diào)用者傳入緩沖區(qū)指針的方案。要求解析器生成的數(shù)據(jù)結(jié)構(gòu)放在一塊連續(xù)的內(nèi)存空間上。這樣,調(diào)用者就可以把指針直接定義成最終方便訪(fǎng)問(wèn)的結(jié)構(gòu)或聯(lián)合。但是提供更充裕的內(nèi)存空間,存放那些內(nèi)部引用的數(shù)據(jù)(比如字符串)。
因?yàn)榻Y(jié)果數(shù)據(jù)區(qū)是由調(diào)用者提供,就不存在數(shù)據(jù)復(fù)制移動(dòng)引起的指針調(diào)整問(wèn)題(調(diào)用者可以自己先分配好)。
最后一個(gè)問(wèn)題是,如何讓調(diào)用者估算數(shù)據(jù)接收區(qū)的大小呢?
很多 Windows API 可以通過(guò)兩次調(diào)用來(lái)完成,第一次空調(diào)用計(jì)算需要的緩沖區(qū)大小,第二次真的去填寫(xiě)數(shù)據(jù)。根據(jù)實(shí)際需求分析過(guò)之后,我認(rèn)為在我們這個(gè)模塊的應(yīng)用上,這樣做是多余的。我們盡可以讓用戶(hù)隨便給一個(gè)估算大小去處理數(shù)據(jù),一旦空間不夠,返回錯(cuò)誤信息。讓用戶(hù)自己擴(kuò)大緩沖區(qū),重新調(diào)用一次即可。
btw, 不斷重試是我們最終認(rèn)可的最 KISS 的方案。一開(kāi)始,我們認(rèn)為讓處理程序自己分配內(nèi)存,并自己使用 realloc 更好。后來(lái)發(fā)現(xiàn),完全是多余的設(shè)計(jì)。因?yàn)椋馕龆M(jìn)制流是 O(1) 的操作,不比估算長(zhǎng)度慢;而往往調(diào)用者都能正確估算接收區(qū)應(yīng)有的長(zhǎng)度,即使簡(jiǎn)單的每次兩倍的方法擴(kuò)展接收區(qū)大小,也不會(huì)浪費(fèi)多少處理時(shí)間。即使他們需要精確分配結(jié)果需要的內(nèi)存塊,盡可以用一個(gè)足夠大的公用緩沖區(qū)接收,然后得到長(zhǎng)度信息,重新在特定內(nèi)存上重來(lái)一次即可。
?
?
寫(xiě)累了。想表達(dá)的也表達(dá)完了。今天到此為止。 :D
ps. 前幾天寫(xiě)了一篇關(guān)于 一種對(duì)漢字更環(huán)保的 Unicode 編碼方案 ,我昨晚花了兩小時(shí)寫(xiě)了個(gè)簡(jiǎn)單的 C 實(shí)現(xiàn)??梢园?UTF-8 或 UTF-16 轉(zhuǎn)換到我自己定義的暫且命名為 UTF-C 的編碼上,也可以轉(zhuǎn)回來(lái)。代碼用的行數(shù)比預(yù)想的要多一些,因?yàn)槲业凸懒?UTF-8 的處理復(fù)雜度(其實(shí)也不復(fù)雜啦)。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元


Comments
開(kāi)年以來(lái)讀到的第一篇雄文,如飲烈酒,甘冽有勁。
Posted by:
寒流 | (24)
February 27, 2011 01:28 AM
strdup是posix的, 不是c庫(kù).呵呵
Posted by:
cc | (23)
August 6, 2010 10:16 PM
哈哈,我們有共同點(diǎn)語(yǔ)言,我大膽地猜測(cè),風(fēng)云大哥一定更喜歡使用寫(xiě)codegen來(lái)解決語(yǔ)言的不足,而不是去依賴(lài)某一語(yǔ)言的特性。
Posted by:
artizc | (22)
February 14, 2009 03:00 PM
方法三稍微修改,函數(shù)內(nèi)部靜態(tài)空間每線(xiàn)程一個(gè),與當(dāng)前線(xiàn)程(ID)捆綁。實(shí)現(xiàn)上以線(xiàn)程ID為key,在數(shù)組或HashMap中存放緩存,實(shí)現(xiàn)頗為簡(jiǎn)單,性能幾乎絲毫不損,也可解決文中大部分問(wèn)題,唯是不可重入。不過(guò)正如云風(fēng)所說(shuō),這個(gè)問(wèn)題可能不是問(wèn)題。
Posted by:
rich | (21)
January 30, 2009 11:04 AM
C 語(yǔ)言標(biāo)準(zhǔn)并沒(méi)有特別限制返回值不能是什么,所以 C 是允許返回struct 作為一種返回復(fù)雜數(shù)據(jù)的方案的。
不知道被以前看過(guò)的哪本書(shū)誤導(dǎo),我覺(jué)得返回 struct 是 C++ 對(duì) C 的一個(gè)擴(kuò)展。(其實(shí)不是)
感謝指出這個(gè)錯(cuò)誤的朋友,我今天仔細(xì)核對(duì)了 ISO C90 和 ISO C99 標(biāo)準(zhǔn)。:D
Posted by:
| (20)
Cloud
January 10, 2009 08:01 PM
您的語(yǔ)言表達(dá)能力有待提高啊。。。
Posted by:
Avin | (19)
January 9, 2009 01:40 PM
"這讓我想到了 MySQL 的 C 語(yǔ)言接口。很多初學(xué) C++ 的程序員,很喜歡把那些 C 接口“封裝”成“漂亮”的 C++ 接口。直接返回 vector 套 map 的多層模板實(shí)例。不知道有多少人干過(guò)?前幾年我?guī)?shí)習(xí)生的時(shí)候反正見(jiàn)過(guò)不少。如果同學(xué)你現(xiàn)在醒悟了,明白這是件巨傻X 的事情,那么握握手,我們有共同理念;否則(C++ 封裝以后不是很“酷”嗎?),我們暫時(shí)沒(méi)有共同語(yǔ)言了。"
呵呵,我也這樣干過(guò)。不過(guò),沒(méi)辦法,誰(shuí)叫咱,那時(shí)候剛學(xué)會(huì)
c++,不管什么東西,都要用我的c++這把小斧頭試試啊。
Posted by:
Anonymous | (18)
January 9, 2009 01:14 PM
windows api的調(diào)用歷來(lái)就是這么做的
Posted by:
Anonymous | (17)
January 8, 2009 05:05 PM
為什么說(shuō)C函數(shù)不能返回結(jié)構(gòu)?
Posted by:
Anonymous | (16)
January 8, 2009 03:42 PM
為什么不看看xdr? sun rpc中的數(shù)據(jù)序列化方式,如果嫌它在線(xiàn)程安全上問(wèn)題太多,那么看看cdr?C++和java都在用它。corba的idl和sun rpc的xdr就是兩個(gè)很好的用DSL定義數(shù)據(jù)然后交給C語(yǔ)言去解析的例子。我覺(jué)得你所討論的這些,在它們的實(shí)現(xiàn)上全有答案。
至于allocator,我只在loki中看見(jiàn)它為小對(duì)象設(shè)計(jì)過(guò),專(zhuān)用于小于64bits的小對(duì)象的分配。我覺(jué)得這方面是花力未必討好的事情。有時(shí)間可以做點(diǎn)別的優(yōu)化。哦,可以借其做內(nèi)存泄露的調(diào)試器。
至于數(shù)據(jù)接收區(qū)的大小,我覺(jué)得stl的stringstream以及string/vector這些都做的很好,根據(jù)需求自動(dòng)增長(zhǎng),還可以讓程序員可以靈活的預(yù)留空間。但是可惜沒(méi)有realloc。不知道你的應(yīng)用具體是什么情形,如果每次都是delete/new而沒(méi)有realloc,對(duì)于性能影響大不。
Posted by:
snnn | (15)
January 8, 2009 02:49 PM
感覺(jué)就是在說(shuō)fgets嘛
Posted by:
zii | (14)
January 8, 2009 12:09 PM
這篇文章提了一個(gè)問(wèn)題:如何用C語(yǔ)言接收被調(diào)用函數(shù)返回的數(shù)據(jù)塊?
答案是:調(diào)用者分配內(nèi)存,然后傳指針給調(diào)用函數(shù)。被調(diào)用者往里面填數(shù)據(jù)。
簡(jiǎn)單的說(shuō)就是:去超市買(mǎi)東西得自備購(gòu)物袋。
Posted by:
D.K | (13)
January 8, 2009 10:19 AM
當(dāng)初我以為指針的數(shù)值等于指針指向的地址
后來(lái)才發(fā)現(xiàn)這兩是不等的。
我到現(xiàn)在還是沒(méi)搞清楚這兩者的關(guān)系。
只是 知道 指針的數(shù)值的地址包含有個(gè)指針頭數(shù)據(jù)才到指針真正的數(shù)據(jù)的地址。
有沒(méi)有語(yǔ)法或函數(shù)得到一個(gè)指針指向數(shù)據(jù)的長(zhǎng)度?
Posted by:
dvaknheo | (12)
January 7, 2009 11:50 PM
感覺(jué)寫(xiě)得很混亂,看得不是很明白,說(shuō)的是對(duì)2進(jìn)制數(shù)據(jù)的描述?
bioware的很多游戲文件都是2進(jìn)制描述的,比如.tlk,從博德1開(kāi)始發(fā)展到現(xiàn)在也沒(méi)淘汰,可以描述一段對(duì)話(huà)的文本(字符串),配音文件(字符串),時(shí)間軸(浮點(diǎn)),顏色什么的,實(shí)現(xiàn)也簡(jiǎn)單,像元數(shù)據(jù),先用一個(gè)數(shù)據(jù)結(jié)構(gòu)描述數(shù)據(jù)結(jié)構(gòu)的結(jié)構(gòu),然后就是按字節(jié)依次填充了
Posted by:
black | (11)
January 7, 2009 08:13 PM
我們公司的模塊間數(shù)據(jù)接口多數(shù)是這種類(lèi)似fread的方案。不過(guò)看RADVISON的代碼就喜歡傳分配器了。
GLib就有點(diǎn)用C寫(xiě)C++的感覺(jué)。
Posted by:
zelor | (10)
January 7, 2009 06:26 PM
傳分配器也可以"要求解析器生成的數(shù)據(jù)結(jié)構(gòu)放在一塊連續(xù)的內(nèi)存空間上"啊,只不過(guò),真的有點(diǎn)怪,明明傳了個(gè)分配器,卻又約定只能調(diào)用一次-_-!或者傳一個(gè)類(lèi)似于realloc的分配器?
Posted by:
天堂的隔壁 | (9)
January 7, 2009 05:25 PM
呵呵,最終的解決辦法 省心啊, 時(shí)間真的很寶貴,省出的時(shí)間可以泡泡妞,灌灌水, 這才叫生活
Posted by:
lbaby | (8)
January 7, 2009 02:35 PM
pool 是一個(gè)內(nèi)部解決方案,能不暴露出來(lái)就不暴露出來(lái)。否則,就需要記住兩個(gè)東西:1. 結(jié)構(gòu)的地址, 2. pool . 并且要記住兩者的關(guān)系。
如果要用的話(huà),最好和別的部分正交化。比如,先創(chuàng)建后 pool 傳遞 buffer 指針進(jìn)去。
至于 pool 的伸展能力,在這里不是必須的。文中已有講述。遵循能減則減的原則,就可以去掉了。
其實(shí)在堆上還是在棧上的問(wèn)題,即使傳分配器,也是都可以兼顧到的 :) 只要愿意弄點(diǎn)奇技淫巧的話(huà)。
傳分配器的大問(wèn)題是,復(fù)雜的數(shù)據(jù)結(jié)構(gòu)是多次分配的,而最終需要一次釋放。這限制了分配器的設(shè)計(jì)(必須設(shè)計(jì)成 pool 那種,而不能是 C 標(biāo)準(zhǔn)的 malloc )
Posted by:
| (7)
Cloud
January 7, 2009 01:43 PM
雖然內(nèi)存管理器等方案也不算壞,但從KISS上說(shuō),傳內(nèi)存確實(shí)用起來(lái)最簡(jiǎn)單,也靈活。(注意云風(fēng)多次強(qiáng)調(diào),內(nèi)存可以在堆里分,也可以在棧上分,似乎要達(dá)到這個(gè)目地傳內(nèi)存就是唯一的方法了)
Posted by:
天堂的隔壁 | (6)
January 7, 2009 12:45 PM
哈哈,新年好,特別謝謝上個(gè)星期云風(fēng)大哥幫我抽中的豆?jié){機(jī),豆?jié){很好喝,呵呵。
平時(shí)也一直在用c語(yǔ)言,說(shuō)些我的看法吧:
1、c是用來(lái)做一些底層和性能要求較高的事情,如果不是很注重性能的話(huà),沒(méi)必要用它。
2、c是可以傳結(jié)構(gòu)體的。c中對(duì)于不定的結(jié)構(gòu)體,可以用void指針。
4、對(duì)于像nginx這類(lèi)代碼,可以借鑒一下它里面的pool的概念。也就是對(duì)于不同的生存期的對(duì)象定義不同的pool,在對(duì)象開(kāi)始時(shí),分配一定的內(nèi)存,在對(duì)象存活時(shí),可以從這個(gè)pool中分配內(nèi)存,不夠的話(huà),擴(kuò)大這個(gè)pool。在對(duì)象消亡時(shí),釋放這個(gè)對(duì)象的pool。pool底層的實(shí)現(xiàn)其實(shí)都是malloc來(lái)做的。不過(guò)它的特定環(huán)境是session這類(lèi)有特定生存期的對(duì)象,與程序的原有設(shè)計(jì)關(guān)系很大,如果是通用的內(nèi)存分配,像這類(lèi)東西就沒(méi)法做到了。
Posted by:
yaoweibin | (5)
January 7, 2009 10:43 AM
GLib很多模塊都是傳構(gòu)造和析構(gòu)函數(shù)指針進(jìn)去,或者至少傳析構(gòu)指針
Posted by:
Anonymous | (4)
January 7, 2009 09:10 AM
至于說(shuō)的引入析構(gòu)函數(shù)指針的問(wèn)題,一般情況下,內(nèi)存都由內(nèi)存分配器管理,不同生命期的對(duì)象使用不同的管理器,如果只牽涉的內(nèi)存的話(huà),我覺(jué)得要注冊(cè)析構(gòu)的情況是很少的
Posted by:
exile | (3)
January 7, 2009 01:40 AM
我覺(jué)得傳入一個(gè)內(nèi)存管理器的方式挺好的啊,比如像nginx這樣的內(nèi)存管理,有點(diǎn)類(lèi)似于Apache的apr_pool,但更適合一般的應(yīng)用,除了性能的提升,更重要的是使用起來(lái)更簡(jiǎn)單,最終統(tǒng)一釋放,也不容易產(chǎn)生內(nèi)存泄露。
Posted by:
exile | (2)
January 7, 2009 01:28 AM
不懂?學(xué)習(xí)!
Posted by:
xinghang | (1)
January 7, 2009 12:23 AM