日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

您知道Linux下C語言編程的一些注意事項(xiàng)嗎_教育

系統(tǒng) 3519 0

云風(fēng)的 BLOG: 一個 C 接口設(shè)計的問題

一個 C 接口設(shè)計的問題

C 語言在本質(zhì)上,參數(shù)傳遞都是值傳遞。不像 Pascal 和 C++ 可以傳引用。這一點(diǎn),使得 C 語言可以保持簡單的設(shè)計,但另一方面也頗為人詬病。

因?yàn)樾阅軉栴},指針不得不被引入。可以說,用 C 語言實(shí)現(xiàn)的軟件,其實(shí)現(xiàn)的 Bug 90% 以上都來至于指針,應(yīng)該是沒有夸大了。當(dāng)然設(shè)計引起的問題或許更為關(guān)鍵一些,那些于指針無關(guān)。

糾結(jié)于性能問題上,層次比較低。可 C 語言就是一個活躍在較低層次的語言,一旦你選擇用它,就不得不關(guān)心性能問題。反過來,把 C 模仿成更高級的語言,倒是有點(diǎn)畫蛇添足了。好了,讓我們來看個實(shí)際的涉及參數(shù)傳遞的相關(guān)問題,用 C 語言該如何設(shè)計。

最近同事在做一個類似 Protocol Buffers 的東西。這個東西做好并不容易,設(shè)計上尤為困難。其中的設(shè)計難點(diǎn):設(shè)計一個合適的 DSL (領(lǐng)域?qū)S谜Z言) 我們討論過很久,也分析了好幾天,但今天不打算談了。揀個小東西說:當(dāng)我們把一個二進(jìn)制結(jié)構(gòu)化數(shù)據(jù)塊解析出來,傳遞到 C 語言中,讓 C 語言可以方便的訪問數(shù)據(jù)結(jié)構(gòu)時,接口如何設(shè)計?


?

這個問題在目標(biāo)語言不是 C 而是更高級的語言(尤其是有 gc 機(jī)制的語言)時,都不是問題。可 C 語言本身是沒有對象概念的。

C 語言有結(jié)構(gòu),但是不具備描述動態(tài)長度的能力;沒有字符串,只有定長的字符數(shù)組;甚至沒有多維數(shù)組,只有一維數(shù)組的數(shù)組。

C 函數(shù)的參數(shù)及返回值可以是結(jié)構(gòu),但在接口設(shè)計中,或許是因?yàn)橹祩鬟f,以及考慮 ABI 的簡潔性的關(guān)系,常常使用結(jié)構(gòu)指針。返回結(jié)構(gòu)指針往往有生命期管理的苦惱。即使到了 C++ 里,允許返回結(jié)果/對象了,可所謂返回值優(yōu)化也是件相當(dāng)讓人困擾的事情(如果你打算完全放棄了解語言的細(xì)節(jié),無視細(xì)微處的性能問題。那么,為什么不考慮使用 Java 或是 Python ,無論什么都比選擇 C++ 強(qiáng))。

對于返回一組復(fù)雜數(shù)據(jù),通常的辦法有些什么?

最常用的方法是,調(diào)用者分配空間,傳遞給處理函數(shù)。由處理函數(shù)反向填寫結(jié)構(gòu)內(nèi)容。這樣的好處是,調(diào)用者可以選擇把空間分配在棧上還是堆上。一點(diǎn)小提示:在語法上,C 語言允許你把一個數(shù)組當(dāng)成指針來傳遞。所以你可以定義一個長度為 1 的結(jié)構(gòu)數(shù)組類型。用起來好看一些。具體見 標(biāo)準(zhǔn)庫中的 setjmp 的定義 。不過作為我個人的理念來說,不太主張在 C 語言設(shè)計的軟件中,為了減少幾次鍵盤輸入,而使用過多的語言特性。

這個方式的缺點(diǎn)是,你很難讓調(diào)用者定義不定的數(shù)據(jù)結(jié)構(gòu)。尤其是在結(jié)構(gòu)里還有對別的結(jié)構(gòu)的引用。

跟這個相似的是接收字符串。最典型的例子是標(biāo)準(zhǔn)庫中的 fgets ,提供一個接收緩沖區(qū)的地址指針,和一個緩沖區(qū)大小。(注:gets 則是一個失敗的設(shè)計)同樣在 Windows 的 API 中,也隨處可見這樣的例子。

第二,就是由函數(shù)自己分配內(nèi)存,交給調(diào)用者去釋放。大家只需要約定內(nèi)存管理的接口即可。標(biāo)準(zhǔn)庫中的 strdup 就是這樣做的,同樣的還有 readline 庫中的 readline 。C 語言統(tǒng)一使用 malloc 管理內(nèi)存,不像 C++ 提供了更靈活(更難控制,更容易出問題?)的 new 操作符重載。所以,給出這個約定并不會增添太多的麻煩。btw, 由于微軟 VC 的 CRT 對 malloc 等實(shí)現(xiàn)的過于糟糕,導(dǎo)致很多 Windows 的軟件自行實(shí)現(xiàn)內(nèi)存管理器。或者在庫中開放自定義內(nèi)存管理器注入的接口。這其實(shí)有點(diǎn)越俎代庖了。gcc 提供的 CRT 里, malloc 性能就相當(dāng)不錯了。

缺點(diǎn)呢?內(nèi)存只能從堆上分配;而且增加了內(nèi)存泄露的隱患;設(shè)計角度上講,也不太干凈。對于復(fù)雜數(shù)據(jù)結(jié)構(gòu),這個方法也無能為力。C 語言里并沒有所謂析構(gòu)函數(shù)的說法。

作為對第二點(diǎn)的一種補(bǔ)充方案,用的人就鳳毛麟角了。那就是給你的系統(tǒng)加入 gc 。實(shí)際上,就是約定另一種內(nèi)存管理方法。我們的項(xiàng)目部分模塊在用,效果還不錯。 gc 庫已經(jīng)開源,請參考這里 。如果信不過這套東西,可以考慮 COM 的機(jī)制:增減引用。COM 旨在建立一種對象模型,可惜 C 語言中沒有對象的概念,在 C 的層面使用 COM ,痛苦了一些。對于粒度比較小的東西,性能也將是問題。

第三種,用的人也比較多。就是在函數(shù)內(nèi)部開一塊靜態(tài)空間,用于數(shù)據(jù)返回。返回的指針指向的數(shù)據(jù)的生命期可以保證到下次調(diào)用同一函數(shù)之前。靜態(tài)空間可以聲明在數(shù)據(jù)段里,也可以在程序初始化時從堆上分配出來,這樣利于在空間不夠的時候擴(kuò)展。至于這塊靜態(tài)空間什么時候釋放的問題,不用太操心。即使不去釋放它們也不用內(nèi)疚。操作系統(tǒng)會幫你回收的,還會比你干的更出色。C 是為了實(shí)現(xiàn) UNIX 而誕生,而 UNIX 的哲學(xué)就是,編寫簡單的程序?qū)P母珊米约旱氖拢尭邔哟蔚某绦颍ㄍǔJ?shell 或動態(tài)語言)去組合它們,讓操作系統(tǒng)去管理它們。在 Windows 上,Unix 編程哲學(xué)未必有用,但大原則沒錯的。

這個方案有另外一個問題,就是函數(shù)不可重入,且有線程安全問題。重入問題可以想辦法避免。線程安全可以用 TLS 解決。老實(shí)說,我個人不看好在 C 語言中使用多線程解決問題。多線程也是違背 Unix 哲學(xué)的。如果你有幾件事情需要協(xié)調(diào)起來做,使用多進(jìn)程;如果你有幾百件事情需要同時來做,考慮換個思路,玩玩 Erlang 啥的。

?


?

回到今天我們面臨的問題。用一種 DSL 來描述一個數(shù)據(jù)結(jié)構(gòu)(比 C 的結(jié)構(gòu)表達(dá)能力更強(qiáng)的),然后生成對應(yīng)語言的解析庫。如果目標(biāo)語言是 C 的話,我們生成的代碼如何返回對 C 程序員友好的結(jié)構(gòu)化數(shù)據(jù)呢?

這讓我想到了 MySQL 的 C 語言接口。很多初學(xué) C++ 的程序員,很喜歡把那些 C 接口“封裝”成“漂亮”的 C++ 接口。直接返回 vector 套 map 的多層模板實(shí)例。不知道有多少人干過?前幾年我?guī)?shí)習(xí)生的時候反正見過不少。如果同學(xué)你現(xiàn)在醒悟了,明白這是件巨傻X 的事情,那么握握手,我們有共同理念;否則(C++ 封裝以后不是很“酷”嗎?),我們暫時沒有共同語言了。

我不是想說 MySQL 的 C 接口設(shè)計的很好,不過是中規(guī)中矩。只是 C++ 不是 C ,C 也不是 C++ 。(話說,上面提到的 C++ 封裝,我也不認(rèn)為是正確的使用 C++ )反復(fù)提及 C++ ,是因?yàn)椋野l(fā)現(xiàn)今天很大比例的 C 程序員其實(shí)是從 C++ 開始啟蒙的,而不是相反。把 C++ 當(dāng)成 C 用的危害其實(shí)比不上把 C 當(dāng)成 C++ 用。前者不過是把汽車開到自行車的速度,至少不怕摔跤了,跑起來還能安全點(diǎn);而后者,非要把自行車踩到高速公路中間,遲早非撞死不可。

最方便 C 程序使用的莫過于傳入一個結(jié)構(gòu)指針,讓庫去解析數(shù)據(jù),填寫這個結(jié)構(gòu)了。

但是,如果結(jié)構(gòu)里有字符串、不定長數(shù)組(通常會根據(jù)前面解析出來的數(shù)據(jù)決定后面的長度,對于 C 的編程技巧來說,允許把結(jié)構(gòu)體的最后一個數(shù)組的長度設(shè)為 0 ,假設(shè)成不定長的,從而減少一次間接的指針引用。但是對于結(jié)構(gòu)中有多個不定長數(shù)組則無法使用這個技巧。)等等的話,就很難避免指針了。

數(shù)據(jù)中一旦出現(xiàn)指針(間接引用別的數(shù)據(jù)),就有內(nèi)存管理問題。

最開始,考慮過一個很 C++ 的方案,傳入一個內(nèi)存管理器。這種設(shè)計在 STL 里就有。所有 STL 的容器都可以指定一個 allocator ,供靈活的管理內(nèi)存。前幾年我倒是認(rèn)為這是個相當(dāng)巧妙的東西。沒有細(xì)想,自定義分配器最終有多大的意義?自定義內(nèi)存管理器,很大程度上是因?yàn)樾室蛩匾鸬摹5阅軉栴}永遠(yuǎn)不是根本問題。制作軟件是為了達(dá)到特定的目的,而軟件開發(fā)的問題更多的是是解決復(fù)雜度問題。往往復(fù)雜度帶來的性能問題更加嚴(yán)重。然后為了解決復(fù)雜度帶來的性能問題去引入更高的復(fù)雜度,出現(xiàn)惡性循環(huán)的可能性非常之大。

即使我們傳入的內(nèi)存管理器(或是直接使用 CRT 里的 malloc,但這樣就沒可能利用堆棧分配空間了),還會面臨新問題,如何回收結(jié)構(gòu)中間接引用的數(shù)據(jù)。引入析構(gòu)函數(shù)指針?OMG 。

后來,我們設(shè)想使用一個內(nèi)部靜態(tài)空間,所有的解析結(jié)果都分配在內(nèi)部,自我管理。這些空間還可以復(fù)用。大部分解析結(jié)果也就是臨時用用,這樣做很方便。而且調(diào)用者不用太關(guān)心數(shù)據(jù)的生命期。

但是,一旦調(diào)用者需要把結(jié)果(一個復(fù)雜結(jié)構(gòu))保存一段時間的話,他就遇到困難。

當(dāng)然,也可能不是困難。當(dāng)我們面對這個設(shè)計難點(diǎn)時,都應(yīng)該向上考慮一層,究竟這是一個問題嗎?我們需要這么用嗎?

調(diào)用者可以自己遍歷這個數(shù)據(jù)結(jié)構(gòu),把他需要的數(shù)據(jù),以自己的方式復(fù)制出來,組織起來。他們需要的是數(shù)據(jù),而不是對數(shù)據(jù)結(jié)構(gòu)完全的拷貝。

仔細(xì)考慮過以后,我們還是發(fā)現(xiàn),保留完整的數(shù)據(jù)結(jié)構(gòu)是有意義的。不像 C++ ,C 沒有對象賦值操作符重載這種語法糖,我也不喜歡用宏去模擬一個出來。增加一個拷貝函數(shù)指針其實(shí)和增添一個析構(gòu)函數(shù)一樣,對 C 來說,不那么漂亮。(當(dāng)然,同時增加了開發(fā)量,我們需要編寫更多的代碼自動生成器)

最終,我們采用了由調(diào)用者傳入緩沖區(qū)指針的方案。要求解析器生成的數(shù)據(jù)結(jié)構(gòu)放在一塊連續(xù)的內(nèi)存空間上。這樣,調(diào)用者就可以把指針直接定義成最終方便訪問的結(jié)構(gòu)或聯(lián)合。但是提供更充裕的內(nèi)存空間,存放那些內(nèi)部引用的數(shù)據(jù)(比如字符串)。

因?yàn)榻Y(jié)果數(shù)據(jù)區(qū)是由調(diào)用者提供,就不存在數(shù)據(jù)復(fù)制移動引起的指針調(diào)整問題(調(diào)用者可以自己先分配好)。

最后一個問題是,如何讓調(diào)用者估算數(shù)據(jù)接收區(qū)的大小呢?

很多 Windows API 可以通過兩次調(diào)用來完成,第一次空調(diào)用計算需要的緩沖區(qū)大小,第二次真的去填寫數(shù)據(jù)。根據(jù)實(shí)際需求分析過之后,我認(rèn)為在我們這個模塊的應(yīng)用上,這樣做是多余的。我們盡可以讓用戶隨便給一個估算大小去處理數(shù)據(jù),一旦空間不夠,返回錯誤信息。讓用戶自己擴(kuò)大緩沖區(qū),重新調(diào)用一次即可。

btw, 不斷重試是我們最終認(rèn)可的最 KISS 的方案。一開始,我們認(rèn)為讓處理程序自己分配內(nèi)存,并自己使用 realloc 更好。后來發(fā)現(xiàn),完全是多余的設(shè)計。因?yàn)椋馕龆M(jìn)制流是 O(1) 的操作,不比估算長度慢;而往往調(diào)用者都能正確估算接收區(qū)應(yīng)有的長度,即使簡單的每次兩倍的方法擴(kuò)展接收區(qū)大小,也不會浪費(fèi)多少處理時間。即使他們需要精確分配結(jié)果需要的內(nèi)存塊,盡可以用一個足夠大的公用緩沖區(qū)接收,然后得到長度信息,重新在特定內(nèi)存上重來一次即可。

?


?

寫累了。想表達(dá)的也表達(dá)完了。今天到此為止。 :D

ps. 前幾天寫了一篇關(guān)于 一種對漢字更環(huán)保的 Unicode 編碼方案 ,我昨晚花了兩小時寫了個簡單的 C 實(shí)現(xiàn)。可以把 UTF-8 或 UTF-16 轉(zhuǎn)換到我自己定義的暫且命名為 UTF-C 的編碼上,也可以轉(zhuǎn)回來。代碼用的行數(shù)比預(yù)想的要多一些,因?yàn)槲业凸懒?UTF-8 的處理復(fù)雜度(其實(shí)也不復(fù)雜啦)。

有興趣的同學(xué)可以看這里

Comments

開年以來讀到的第一篇雄文,如飲烈酒,甘冽有勁。

strdup是posix的, 不是c庫.呵呵

哈哈,我們有共同點(diǎn)語言,我大膽地猜測,風(fēng)云大哥一定更喜歡使用寫codegen來解決語言的不足,而不是去依賴某一語言的特性。

方法三稍微修改,函數(shù)內(nèi)部靜態(tài)空間每線程一個,與當(dāng)前線程(ID)捆綁。實(shí)現(xiàn)上以線程ID為key,在數(shù)組或HashMap中存放緩存,實(shí)現(xiàn)頗為簡單,性能幾乎絲毫不損,也可解決文中大部分問題,唯是不可重入。不過正如云風(fēng)所說,這個問題可能不是問題。

C 語言標(biāo)準(zhǔn)并沒有特別限制返回值不能是什么,所以 C 是允許返回struct 作為一種返回復(fù)雜數(shù)據(jù)的方案的。

不知道被以前看過的哪本書誤導(dǎo),我覺得返回 struct 是 C++ 對 C 的一個擴(kuò)展。(其實(shí)不是)

感謝指出這個錯誤的朋友,我今天仔細(xì)核對了 ISO C90 和 ISO C99 標(biāo)準(zhǔn)。:D

您的語言表達(dá)能力有待提高啊。。。

"這讓我想到了 MySQL 的 C 語言接口。很多初學(xué) C++ 的程序員,很喜歡把那些 C 接口“封裝”成“漂亮”的 C++ 接口。直接返回 vector 套 map 的多層模板實(shí)例。不知道有多少人干過?前幾年我?guī)?shí)習(xí)生的時候反正見過不少。如果同學(xué)你現(xiàn)在醒悟了,明白這是件巨傻X 的事情,那么握握手,我們有共同理念;否則(C++ 封裝以后不是很“酷”嗎?),我們暫時沒有共同語言了。"

呵呵,我也這樣干過。不過,沒辦法,誰叫咱,那時候剛學(xué)會

c++,不管什么東西,都要用我的c++這把小斧頭試試啊。

windows api的調(diào)用歷來就是這么做的

為什么說C函數(shù)不能返回結(jié)構(gòu)?

為什么不看看xdr? sun rpc中的數(shù)據(jù)序列化方式,如果嫌它在線程安全上問題太多,那么看看cdr?C++和java都在用它。corba的idl和sun rpc的xdr就是兩個很好的用DSL定義數(shù)據(jù)然后交給C語言去解析的例子。我覺得你所討論的這些,在它們的實(shí)現(xiàn)上全有答案。

至于allocator,我只在loki中看見它為小對象設(shè)計過,專用于小于64bits的小對象的分配。我覺得這方面是花力未必討好的事情。有時間可以做點(diǎn)別的優(yōu)化。哦,可以借其做內(nèi)存泄露的調(diào)試器。

至于數(shù)據(jù)接收區(qū)的大小,我覺得stl的stringstream以及string/vector這些都做的很好,根據(jù)需求自動增長,還可以讓程序員可以靈活的預(yù)留空間。但是可惜沒有realloc。不知道你的應(yīng)用具體是什么情形,如果每次都是delete/new而沒有realloc,對于性能影響大不。

感覺就是在說fgets嘛

這篇文章提了一個問題:如何用C語言接收被調(diào)用函數(shù)返回的數(shù)據(jù)塊?

答案是:調(diào)用者分配內(nèi)存,然后傳指針給調(diào)用函數(shù)。被調(diào)用者往里面填數(shù)據(jù)。

簡單的說就是:去超市買東西得自備購物袋。

當(dāng)初我以為指針的數(shù)值等于指針指向的地址

后來才發(fā)現(xiàn)這兩是不等的。

我到現(xiàn)在還是沒搞清楚這兩者的關(guān)系。

只是 知道 指針的數(shù)值的地址包含有個指針頭數(shù)據(jù)才到指針真正的數(shù)據(jù)的地址。

有沒有語法或函數(shù)得到一個指針指向數(shù)據(jù)的長度?

感覺寫得很混亂,看得不是很明白,說的是對2進(jìn)制數(shù)據(jù)的描述?

bioware的很多游戲文件都是2進(jìn)制描述的,比如.tlk,從博德1開始發(fā)展到現(xiàn)在也沒淘汰,可以描述一段對話的文本(字符串),配音文件(字符串),時間軸(浮點(diǎn)),顏色什么的,實(shí)現(xiàn)也簡單,像元數(shù)據(jù),先用一個數(shù)據(jù)結(jié)構(gòu)描述數(shù)據(jù)結(jié)構(gòu)的結(jié)構(gòu),然后就是按字節(jié)依次填充了

我們公司的模塊間數(shù)據(jù)接口多數(shù)是這種類似fread的方案。不過看RADVISON的代碼就喜歡傳分配器了。

GLib就有點(diǎn)用C寫C++的感覺。

傳分配器也可以"要求解析器生成的數(shù)據(jù)結(jié)構(gòu)放在一塊連續(xù)的內(nèi)存空間上"啊,只不過,真的有點(diǎn)怪,明明傳了個分配器,卻又約定只能調(diào)用一次-_-!或者傳一個類似于realloc的分配器?

呵呵,最終的解決辦法 省心啊, 時間真的很寶貴,省出的時間可以泡泡妞,灌灌水, 這才叫生活

pool 是一個內(nèi)部解決方案,能不暴露出來就不暴露出來。否則,就需要記住兩個東西:1. 結(jié)構(gòu)的地址, 2. pool . 并且要記住兩者的關(guān)系。

如果要用的話,最好和別的部分正交化。比如,先創(chuàng)建后 pool 傳遞 buffer 指針進(jìn)去。

至于 pool 的伸展能力,在這里不是必須的。文中已有講述。遵循能減則減的原則,就可以去掉了。

其實(shí)在堆上還是在棧上的問題,即使傳分配器,也是都可以兼顧到的 :) 只要愿意弄點(diǎn)奇技淫巧的話。

傳分配器的大問題是,復(fù)雜的數(shù)據(jù)結(jié)構(gòu)是多次分配的,而最終需要一次釋放。這限制了分配器的設(shè)計(必須設(shè)計成 pool 那種,而不能是 C 標(biāo)準(zhǔn)的 malloc )

雖然內(nèi)存管理器等方案也不算壞,但從KISS上說,傳內(nèi)存確實(shí)用起來最簡單,也靈活。(注意云風(fēng)多次強(qiáng)調(diào),內(nèi)存可以在堆里分,也可以在棧上分,似乎要達(dá)到這個目地傳內(nèi)存就是唯一的方法了)

哈哈,新年好,特別謝謝上個星期云風(fēng)大哥幫我抽中的豆?jié){機(jī),豆?jié){很好喝,呵呵。

平時也一直在用c語言,說些我的看法吧:

1、c是用來做一些底層和性能要求較高的事情,如果不是很注重性能的話,沒必要用它。

2、c是可以傳結(jié)構(gòu)體的。c中對于不定的結(jié)構(gòu)體,可以用void指針。

4、對于像nginx這類代碼,可以借鑒一下它里面的pool的概念。也就是對于不同的生存期的對象定義不同的pool,在對象開始時,分配一定的內(nèi)存,在對象存活時,可以從這個pool中分配內(nèi)存,不夠的話,擴(kuò)大這個pool。在對象消亡時,釋放這個對象的pool。pool底層的實(shí)現(xiàn)其實(shí)都是malloc來做的。不過它的特定環(huán)境是session這類有特定生存期的對象,與程序的原有設(shè)計關(guān)系很大,如果是通用的內(nèi)存分配,像這類東西就沒法做到了。

GLib很多模塊都是傳構(gòu)造和析構(gòu)函數(shù)指針進(jìn)去,或者至少傳析構(gòu)指針

至于說的引入析構(gòu)函數(shù)指針的問題,一般情況下,內(nèi)存都由內(nèi)存分配器管理,不同生命期的對象使用不同的管理器,如果只牽涉的內(nèi)存的話,我覺得要注冊析構(gòu)的情況是很少的

我覺得傳入一個內(nèi)存管理器的方式挺好的啊,比如像nginx這樣的內(nèi)存管理,有點(diǎn)類似于Apache的apr_pool,但更適合一般的應(yīng)用,除了性能的提升,更重要的是使用起來更簡單,最終統(tǒng)一釋放,也不容易產(chǎn)生內(nèi)存泄露。

不懂?學(xué)習(xí)!

您知道Linux下C語言編程的一些注意事項(xiàng)嗎_教育中國


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 莒南县| 鹿泉市| 贵港市| 平谷区| 共和县| 盐山县| 黄龙县| 固安县| 长阳| 富裕县| 临江市| 云和县| 涞水县| 锡林浩特市| 红河县| 千阳县| 田东县| 大庆市| 育儿| 务川| 南部县| 井冈山市| 库车县| 成都市| 永清县| 瑞昌市| 芦溪县| 莲花县| 全州县| 丰宁| 东方市| 博湖县| 贵德县| 南投县| 中阳县| 商丘市| 黄山市| 咸宁市| 汉川市| 桦川县| 邯郸县|