【譯】Go 語言 Malloc 的慣用語法

我終于又開始使用 Go 語言編程了。雖然我在前兩年多的時間里積極參與這個項目馋艺,但從 2012 年起,我就基本沒有參加過這個項目迈套。最初捐祠,我之所以做出貢獻,是因為我是貝爾實驗室 Plan 9(操作系統(tǒng)) 和 FreeBSD 的粉絲桑李。我喜歡可用的踱蛀、基于 csp 的語言,但是 Go 最初的版本只能在 Linux 和 OS X 上運行贵白。那時候我只有 FreeBSD 系統(tǒng)率拒,因此,我將編譯器工具鏈禁荒、運行時和標準庫移植到 FreeBSD (有很多調試成果來自 Russ Cox )

然而猬膨,過去我的大部分工作都是在低延遲的系統(tǒng)軟件上,它們大部分是用 C 語言編寫的,自2007年起呛伴,我所有的雇主都不再支持 FreeBSD勃痴。因為我并沒有真正的機會用 Go 語言去編寫新的軟件,而且我最終也不再對維護一個操作系統(tǒng)的知識感興趣热康,我只是為了好玩沛申,所以我對 Go 語言的使用和貢獻都被擱置了。

現在我在谷歌工作姐军,我終于有機會用 Go 語言寫代碼了污它。雖然我仍然喜歡這門語言,但有一些經驗報告,例如風格那樣的東西結果阻止了我在過去的 5~6 年里使用這門語言,而我現在覺得有些麻煩衫贬。在一些同事的建議下德澈,我想我應該至少記錄下其中的一個。

我想念的一件事情是 C 語言 malloc 那樣的習慣用法固惯。在 C 語言中梆造,分配的內存通常是對 mallocs -family 函數的調用,它至少給您足夠的內存來完成您想要的功能葬毫,或者并不給您分配內存镇辉。慣用語法大致是這樣的:

 T *p = malloc(sizeof *p);

注意,p (T *) 的類型只出現一次贴捡。這一行代碼利用了它的操作數時的大小忽肛,并且顯示了一個指針 —— 這其實是沒有發(fā)生的事情,因為操作符 sizeof 的結果必須在編譯時是可識別的烂斋。這種編程語言而不是語法定義的結果是 sizeof 產生了指向對象的大小;它不會變成運行時的東西屹逛。C 語言的好處是,如果我改變用 T 表示的類型汛骂,我只改變聲明或定義中的類型罕模。在 site(s) 中不需要做任何更改,指針對象被分配給內存分配的結果帘瞭。上面的示例很簡單淑掌,讓我們考慮一個更復雜的情況,結構成員指向某種類型:

 struct set {
     size_t cap;
     size_t nmemb;
     int members[];
 }
 
 struct set *
 set_create(size_t sz)
 {
     struct set *n = malloc(sizeof *n + (sz * sizeof *n->members));
     if (n == NULL) {
         return NULL;
     }
 
     n->members = malloc(sz * sizeof *n->members);
     if (n->members == NULL) {
         free(n);
         return NULL;
     }
 
     n->cap = sz;
     n->nmemb = 0;
 
     return n;
 }

如果以后我們想要更改 struct set 來支持除 int 以外的成員蝶念,我們可能會將成員更改為一個union抛腕,并添加一些 enum 類型來指定一些我們想要添加的字段。我們可以在不改變 set_create 中的任何代碼的情況下做到這一點媒殉。

每次我使用 Go 語言創(chuàng)建了一些結構類型,當需要嵌入一些像 slice 和 map 那樣需要分配內存的字段的時候都讓我很抓狂担敌。在 Go 中,我們被迫重復表達我們想要分配的東西的類型,盡管編譯器熟知這種類型而且類型推斷是符合語言習慣的(試想一下如這樣的表達式 a:= b ),我有時不得不深究一下嵌入字段的類型是什么。讓我們來看看在創(chuàng)建一個嵌入了 map 的結構體所涉及的內容:

 type NamedMap struct {
     name string
     m    map[string]string
 }
 
 func NewNamedMap(name string) *NamedMap {
     return &NamedMap{name: name, m: map[string]string{}}
 }
 

我們還可以在 NewNamedMap 中使用 make 适袜,但是仍然保留了return &NamedMap{name: name, m: make(map[string]string)} — 再次柄错,重復它的類型。經過深思熟慮的代碼苦酱,應該只有一個(額外的)地方需要我們指定類型來分配它售貌,但是當類型改變時,這仍然需要多處改動代碼疫萤。當我在做原型的時候颂跨,這就會讓我抓狂,而且我還沒有充分考慮到我需要保存在 map 中的狀態(tài)扯饶。我發(fā)現在很多地方需要自己手動將 map[string]string 更改為 map[string]T恒削,每次我需要更改多行代碼時池颈,它都會使我感到困擾。

有人可能會說钓丰,在寫代碼之前躯砰,我應該多考慮一下我需要什么,那樣會更好携丁。但我仍然會反駁說琢歇,在項目的生命周期中開發(fā)額外的狀態(tài)需求并不少見,比如在上面的例子中梦鉴。隨著時間的推移李茫,系統(tǒng)的約束也可能會發(fā)生變化,這樣一種最初非常好的類型最終會變得不可用肥橙。在 Go 中魄宏,set 結構可能是這樣的:

 type Set struct {
     nmemb   int
     cap     int
     members []int
 }
 
 func NewSet(sz int) *Set {
     return &Set{cap: sz, members: []int{}}
 }

你可能會問為什么我不只是用一個 slice,答案是這是一個演示這個問題的簡單例子存筏。不管怎樣宠互,我們以后可能想要支持不同類型的 slice胜卤,那樣我們又遇到了之前的問題耕陷。set 中如果有一個 slice,我們可能可以忽略初始化,假設我們在添加后只從 slice 中讀取藕溅。由于形如 map 和 channel 那樣的類型,因為我們必須在使用前進行分配继榆,所以會使得情況更加復雜巾表。那么在某個地方重復輸入信息并不罕見。

我不知道該如何解決這個問題略吨。對于復合文本集币,可能可以添加如下語法:

 return &Set{cap: sz, members: Set.members{}}

如果你有一個指向 slice 的指針:

 return &Set{cap: sz, members: &Set.members{}}

我不知道我是否喜歡這些復合文字語法。在 C 語言中翠忠,為支持 sizeof 行為而進行的修正感覺更有表現力和明顯:

 return &Set{cap: sz, members: make(Set.members)}

但也許這只是我用 C 語言編程的時間太長了鞠苟。

我不知道改變新的有相似的行為是有用的還是有價值的;我懷疑它的使用是不尋常的。在任何情況下秽之,我都清楚這將減少重構軟件以及編寫新的軟件的開銷当娱。

這個問題并不是那么糟糕,但修復它肯定會讓我覺得更好考榨。在很多情況下跨细,Go已經比 C 和 C++ (我至今無法忍受) 更有表現力了。我認為河质,如果在語言中添加了對分配類型的推斷的支持冀惭,那么 Go 語言對 C 系統(tǒng)程序員來說就會更有吸引力震叙,因為除了 GC ,他們還有其他堅持的理由散休。(就我個人而言媒楼,我還希望看到一個關于支持系統(tǒng)級并發(fā)的更好的事情,但最好是單獨發(fā)布戚丸。)

我編輯了這篇文章匣砖,以修復 C 示例中的一個錯誤。當我最初編寫這個示例時昏滴,struct set 沒有使用靈活的數組成員猴鲫。Anmol Sethi 寫信詢問這個特性,并指出我錯誤地分配和再次分配給了FAM谣殊。我忘記了要刪除那些代碼拂共。


via: https://9vx.org/post/a-malloc-idiom-in-go/
作者:Devon
譯者:SergeyChang

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市姻几,隨后出現的幾起案子宜狐,更是在濱河造成了極大的恐慌,老刑警劉巖蛇捌,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抚恒,死亡現場離奇詭異,居然都是意外死亡络拌,警方通過查閱死者的電腦和手機俭驮,發(fā)現死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來春贸,“玉大人混萝,你說我怎么就攤上這事∑妓。” “怎么了逸嘀?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長允粤。 經常有香客問我崭倘,道長,這世上最難降的妖魔是什么类垫? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任司光,我火速辦了婚禮,結果婚禮上阔挠,老公的妹妹穿的比我還像新娘飘庄。我一直安慰自己,他們只是感情好购撼,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布跪削。 她就那樣靜靜地躺著谴仙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪碾盐。 梳的紋絲不亂的頭發(fā)上晃跺,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機與錄音毫玖,去河邊找鬼掀虎。 笑死,一個胖子當著我的面吹牛付枫,可吹牛的內容都是我干的烹玉。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼阐滩,長吁一口氣:“原來是場噩夢啊……” “哼二打!你這毒婦竟也來了?” 一聲冷哼從身側響起掂榔,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤继效,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后装获,有當地人在樹林里發(fā)現了一具尸體瑞信,經...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年穴豫,在試婚紗的時候發(fā)現自己被綠了凡简。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡绩郎,死狀恐怖潘鲫,靈堂內的尸體忽然破棺而出翁逞,到底是詐尸還是另有隱情肋杖,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布挖函,位于F島的核電站状植,受9級特大地震影響,放射性物質發(fā)生泄漏怨喘。R本人自食惡果不足惜津畸,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望必怜。 院中可真熱鬧肉拓,春花似錦、人聲如沸梳庆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至驻售,卻和暖如春露久,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背欺栗。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工毫痕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人迟几。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓消请,卻偏偏與公主長得像,于是被迫代替她去往敵國和親类腮。 傳聞我的和親對象是個殘疾皇子梯啤,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內容

  • Go入門 Go介紹 部落圖鑒之Go:爹好還這么努力? 環(huán)境配置 安裝 下載源碼編譯安裝 下載相應平臺的安裝包安裝 ...
    齊天大圣李圣杰閱讀 4,625評論 0 26
  • 基礎 一開始存哲,將學習關于語言的所有基礎內容因宇。學習如何基于已有類型定義新的類型:蓋了結構體、數組祟偷、slice 和 m...
    張洋銘Ocean閱讀 1,320評論 0 1
  • 你們有個好媽媽 你們的媽媽是超人 我的媽媽又不是超人 她不偉大 不會在下雨天去學校給我送傘 她不漂亮 不能讓我跟同...
    JaryYang閱讀 384評論 3 5
  • 2016-06-15 無尾鹿 初次發(fā)表于微信公眾號“喜餅不是餅” 這是今天早上路邊的青草地察滑,雨后的陽光下,露珠閃著...
    喜餅閱讀 350評論 0 1
  • 今日悠閑的在家休息了一天修肠,什么事情都有做一點贺辰,定完了火車票,看了看cpa嵌施,還健了身饲化。看CPA時有一種按耐不住的浮躁...
    帥氣梁朝偉閱讀 182評論 0 0