<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          掌握 cgo 的字符串函數(shù)

          共 4070字,需瀏覽 9分鐘

           ·

          2021-05-05 10:54

          點(diǎn)擊上方藍(lán)色“Go語(yǔ)言中文網(wǎng)”關(guān)注,每天一起學(xué) Go

          cgo[1] 的大量文檔都提到過(guò),它提供了四個(gè)用于轉(zhuǎn)換 Go 和 C 類(lèi)型的字符串的函數(shù),都是通過(guò)復(fù)制數(shù)據(jù)來(lái)實(shí)現(xiàn)。在 CGo 的文檔中有簡(jiǎn)潔的解釋?zhuān)艺J(rèn)為解釋得太簡(jiǎn)潔了,因?yàn)槲臋n只涉及了定義中的某些特定字符串,而忽略了兩個(gè)很重要的注意事項(xiàng)。我曾經(jīng)踩過(guò)這里的坑,現(xiàn)在我要詳細(xì)解釋一下。

          四個(gè)函數(shù)分別是:

          func C.CString(string) *C.char
          func C.GoString(*C.char) string
          func C.GoStringN(*C.char, C.int) string
          func C.GoBytes(unsafe.Pointer, C.int) []byte

          C.CString() 等價(jià)于 C 的 strdup(),像文檔中提到的那樣,把 Go 的字符串復(fù)制為可以傳遞給 C 函數(shù)的 C 的 char *。很討厭的一件事是,由于 Go 和 CGo 類(lèi)型的定義方式,調(diào)用 C.free 時(shí)需要做一個(gè)轉(zhuǎn)換:

          cs := C.CString("a string")
          C.free(unsafe.Pointer(cs))

          請(qǐng)留意,Go 字符串中可能嵌入了 \0 字符,而 C 字符串不會(huì)。如果你的 Go 字符串中有 \0 字符,當(dāng)你調(diào)用 C.CString() 時(shí),C 代碼會(huì)從 \0 字符處截?cái)嗄愕淖址_@往往不會(huì)被注意到,但有時(shí)文本并不保證不含 null 字符[2]

          C.GoString() 也等價(jià)于 strdup(),但與 C.CString() 相反,是把 C 字符串轉(zhuǎn)換為 Go 字符串。你可以用它定義結(jié)構(gòu)體的字段,或者是聲明為 C 的 char *(在 Go 中叫 *C.cahr) 的其他變量,抑或其他的一些變量(我們后面會(huì)看到)。

          C.GoStringN() 等價(jià)于 C 的 memmove(),與 C 中普通的字符串函數(shù)不同。它把整個(gè) N 長(zhǎng)度的 C buffer 復(fù)制為一個(gè) Go 字符串,不單獨(dú)處理 null 字符。再詳細(xì)點(diǎn),它也通過(guò)復(fù)制來(lái)實(shí)現(xiàn)。如果你有一個(gè)定義為 char feild[64] 的結(jié)構(gòu)體的字段,然后調(diào)用了 C.GoStringN(&field, 64),那么你得到的 Go 字符串一定是 64 個(gè)字符,字符串的末尾有可能是一串 \0 字符。

          (我認(rèn)為這是 cgo 文檔中的一個(gè) bug。它宣稱(chēng) GoStringN 的入?yún)⑹且粋€(gè) C 的字符串,但實(shí)際上很明顯不是,因?yàn)?C 的字符串不能以 null 字符結(jié)束,而 GoStringN 不會(huì)在 null 字符處結(jié)束處理。)

          C.GoBytes()C.GoStringN() 的另一個(gè)版本,不返回 string 而是返回 []byte。它沒(méi)有宣稱(chēng)以 C 字符串作為入?yún)ⅲ鼉H僅是對(duì)整個(gè) buffer 做了內(nèi)存拷貝。

          如果你要拷貝的東西不是以 null 字符結(jié)尾的 C 字符串,而是固定長(zhǎng)度的 memory buffer,那么 C.GoString() 正好能滿(mǎn)足需求;它避開(kāi)了 C 中傳統(tǒng)的問(wèn)題處理不是 C 字符串的 ’string‘[3]。然而,如果你要處理定義為 char field[N] 的結(jié)構(gòu)體字段這種限定長(zhǎng)度的 C 字符串時(shí),這些函數(shù)都不能滿(mǎn)足需求。

          傳統(tǒng)語(yǔ)義的結(jié)構(gòu)體中固定長(zhǎng)度的字符串變量,定義為 char field[N] 的字段,以及“包含一個(gè)字符串”等描述,都表示當(dāng)且僅當(dāng)字符串有足夠空間時(shí)以 null 字符結(jié)尾,換句話說(shuō),字符串最多有 N-1 個(gè)字符。如果字符串正好有 N 個(gè)字符,那么它不會(huì)以 null 字符結(jié)尾。這是 C 代碼中諸多 bug 的根源[4],也不是一個(gè)好的 API,但我們卻擺脫不了這個(gè) API。每次我們遇到這樣的字段,文檔不會(huì)明確告訴你字段的內(nèi)容并不一定是 null 字符結(jié)尾的,你需要自己假設(shè)你有這種 API。

          C.GoString()C.GoStringN() 都不能正確處理這些字段。使用 GoStringN() 相對(duì)來(lái)說(shuō)出錯(cuò)更少;它僅僅返回一個(gè)末尾有一串 \0 字符長(zhǎng)度為 N 的 Go 字符串(如果你僅僅是把這些字段打印出來(lái),那么你可能不會(huì)留意到;我經(jīng)常干這種事)。使用有誘惑力的 GoString() 更是引狼入室,因?yàn)樗鼉?nèi)部會(huì)對(duì)入?yún)⒆?strlen();如果字符末尾沒(méi)有 null 字符,strlen() 會(huì)訪問(wèn)越界的內(nèi)存地址。如果你走運(yùn),你得到的 Go 字符串末尾會(huì)有大量的垃圾。如果你不走運(yùn),你的 Go 程序出現(xiàn)段錯(cuò)誤,因?yàn)?strlen() 訪問(wèn)了未映射的內(nèi)存地址。

          (總的來(lái)說(shuō),如果字符串末尾出現(xiàn)了大量垃圾,通常意味著在某處有不含結(jié)束符的 C 字符串。)

          你需要的是與 C 的 strndup() 等價(jià)的 Go 函數(shù),以此來(lái)確保復(fù)制不超過(guò) N 個(gè)字符且在 null 字符處終止。下面是我寫(xiě)的版本,不保證無(wú)錯(cuò)誤:

          func strndup(cs *C.char, len int) string {
             s := C.GoStringN(cs, C.int(len))
             i := strings.IndexByte(s, 0)
             if i == -1 {
                return s
             }
             return C.GoString(cs)
          }

          由于有 Go 的字符串怎樣占用內(nèi)存[5]的問(wèn)題,這段代碼做了些額外的工作來(lái)最小化額外的內(nèi)存占用。你可能想用另一種方法,返回一個(gè) GoStringN() 字符串的切片。你也可以寫(xiě)復(fù)雜的代碼,根據(jù) i 和 len 的不同來(lái)決定選用哪種方法。

          更新:Ian Lance Taylor 給我展示了份更好的代碼[6]

          func strndup(cs *C.char, len int) string {
             return C.GoStringN(cs, C.int(C.strnlen(cs, C.size_t(len))))
          }

          是的,這里有大量的轉(zhuǎn)換。這篇文章就是你看到的 Go 和 Gco 類(lèi)型的結(jié)合。


          via: https://utcc.utoronto.ca/~cks/space/blog/programming/GoCGoStringFunctions

          作者:ChrisSiebenmann[7]譯者:lxbwolf[8]校對(duì):polaris1119[9]

          本文由 GCTT[10] 原創(chuàng)編譯,Go 中文網(wǎng)[11] 榮譽(yù)推出

          參考資料

          [1]

          cgo: https://github.com/golang/go/wiki/cgo

          [2]

          有時(shí)文本并不保證不含 null 字符: https://utcc.utoronto.ca/~cks/space/blog/programming/BeSureItsACString

          [3]

          處理不是 C 字符串的 ’string‘: https://utcc.utoronto.ca/~cks/space/blog/programming/BeSureItsACString

          [4]

          N]` 的字段,以及“包含一個(gè)字符串”等描述,都表示當(dāng)且僅當(dāng)字符串有足夠空間時(shí)以 null 字符結(jié)尾,換句話說(shuō),字符串最多有 N-1 個(gè)字符。如果字符串正好有 N 個(gè)字符,那么它不會(huì)以 null 字符結(jié)尾。這是 [C 代碼中諸多 bug 的根源: https://utcc.utoronto.ca/~cks/space/blog/programming/UnixAPIMistake

          [5]

          Go 的字符串怎樣占用內(nèi)存: https://utcc.utoronto.ca/~cks/space/blog/programming/GoStringsMemoryHolding

          [6]

          Ian Lance Taylor 給我展示了份更好的代碼: https://github.com/golang/go/issues/12428#issuecomment-136581154

          [7]

          ChrisSiebenmann: https://utcc.utoronto.ca/~cks/space/People/ChrisSiebenmann

          [8]

          lxbwolf: https://github.com/lxbwolf

          [9]

          polaris1119: https://github.com/polaris1119

          [10]

          GCTT: https://github.com/studygolang/GCTT

          [11]

          Go 中文網(wǎng): https://studygolang.com/



          推薦閱讀


          福利

          我為大家整理了一份從入門(mén)到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門(mén)看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù) ebook 獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。

          瀏覽 83
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  99er‘在线直播观看 | 色婷婷在线无码精品 | 大粗鸡巴久久久 | 在线观看肏屄视频 | 四虎黄色按摩 |