真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

學(xué)習(xí)筆記-go程序?qū)嶓w

Go 語言中的程序?qū)嶓w包括:變量、常量、函數(shù)、結(jié)構(gòu)體和接口。
Go 語言是靜態(tài)類型的編程語言,所以我們?cè)诼暶髯兞炕虺A康臅r(shí)候,都需要指定它們的類型,或者給予足夠的信息,這樣才可以讓 Go 語言能夠推導(dǎo)出它們的類型,在 Go 語言中,變量的類型可以是其預(yù)定義的那些類型,也可以是程序自定義的函數(shù)、結(jié)構(gòu)體或接口。常量的合法類型不多,只能是那些 Go 語言預(yù)定義的基本類型。它的聲明方式也更簡(jiǎn)單一些。

網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)公司!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、小程序開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了城區(qū)免費(fèi)建站歡迎大家使用!

1、問題:聲明變量有幾種方式?

package main

import (
    "flag"
    "fmt"
)

func main() {
    var name string    //var name string這種聲明變量name的方式         // [1]
    flag.StringVar(&name, "name", "everyone", "The greeting object.") // [2]

    // 方式1。
    //var name = flag.String("name", "everyone", "The greeting object.")

    // 方式2。
    //name := flag.String("name", "everyone", "The greeting object.")

    flag.Parse()
    fmt.Printf("Hello, %v!\n", name)

    // 適用于方式1和方式2。
    //fmt.Printf("Hello, %v!\n", *name)
}

第一種方式中的代碼在聲明變量name的同時(shí),還為它賦了值,而這時(shí)聲明中并沒有顯式指定name的類型。這里利用了 Go 語言自身的類型推斷,而省去了對(duì)該變量的類型的聲明。

把被調(diào)用的函數(shù)由flag.StringVar改為flag.String,傳參的列表也需要隨之修改,這是為了[1]和[2]處代碼合并的準(zhǔn)備工作。

注意,flag.String函數(shù)返回的結(jié)果值的類型是string而不是string。類型string代表的是字符串的指針類型,而不是字符串類型。因此,這里的變量name代表的是一個(gè)指向字符串值的指針。

我們可以通過操作符把這個(gè)指針指向的字符串值取出來了。因此,在這種情況下,那個(gè)被用來打印內(nèi)容的函數(shù)調(diào)用就需要微調(diào)一下,把其中的參數(shù)name改為name,即:fmt.Printf("Hello, %v!\n", *name)。

第二種方式與第一種方式非常類似,它基于第一種方式的代碼,賦值符號(hào)=右邊的代碼不動(dòng),左邊只留下name,再把=變成:=

學(xué)習(xí)筆記-go程序?qū)嶓w

2、 上面列出了幾種方式,區(qū)別在哪里?

var name = flag.String("name", "everyone", "The greeting object.")

第一種方式中的代碼在聲明變量name的同時(shí),還為它賦了值,而這時(shí)聲明中并沒有顯式指定name的類型。

這里利用了 Go 語言自身的類型推斷,而省去了對(duì)該變量的類型的聲明。

簡(jiǎn)單地說,類型推斷是一種編程語言在編譯期自動(dòng)解釋表達(dá)式類型的能力。什么是表達(dá)式?詳細(xì)的解釋你可以參看 Go 語言規(guī)范中的表達(dá)式https://golang.google.cn/ref/spec#Expressions 和表達(dá)式語句https://golang.google.cn/ref/spec#Expression_statements 章節(jié)

表達(dá)式類型就是對(duì)表達(dá)式進(jìn)行求值后得到結(jié)果的類型。Go 語言中的類型推斷是很簡(jiǎn)約的,這也是 Go 語言整體的風(fēng)格。

它只能用于對(duì)變量或常量的初始化,就像上述回答中描述的那樣。對(duì)flag.String函數(shù)的調(diào)用其實(shí)就是一個(gè)調(diào)用表達(dá)式,而這個(gè)表達(dá)式的類型是*string,即字符串的指針類型。

這也是調(diào)用flag.String函數(shù)后得到結(jié)果的類型。隨后,Go 語言把這個(gè)調(diào)用了flag.String函數(shù)的表達(dá)式類型,直接作為了變量name的類型,這就是“推斷”一詞所指代的操作了。

name := flag.String("name", "everyone", "The greeting object.")

至于第二種方式所用的短變量聲明,實(shí)際上就是 Go 語言的類型推斷再加上一點(diǎn)點(diǎn)語法糖。

我們只能在函數(shù)體內(nèi)部使用短變量聲明。在編寫if、for或switch語句的時(shí)候,我們經(jīng)常把它安插在初始化子句中,并用來聲明一些臨時(shí)的變量。而相比之下,第一種方式更加通用,它可以被用在任何地方。

3、 Go 語言的類型推斷可以帶來哪些好處?

先看一段代碼:

package main

import (
    "flag"
    "fmt"
)

func main() {
    var name = getTheFlag()
    flag.Parse()
    fmt.Printf("Hello, %v!\n", *name)
}

func getTheFlag() *string {
    return flag.String("name", "everyone", "The greeting object.")
}

//上面函數(shù)的實(shí)現(xiàn)也可以是這樣的。
//func getTheFlag() *int {
//  return flag.Int("num", 1, "The number of greeting object.")
//}
go run demo8.go -name huaihe
Hello, huaihe!

name能不能是數(shù)字呢?

package main

import (
    "flag"
    "fmt"
)

func main() {
    var name = getTheFlag()
    flag.Parse()
    fmt.Printf("Hello, %v!\n", *name)
}

// func getTheFlag() *string {
//  return flag.String("name", "everyone", "The greeting object.")
// }

//上面函數(shù)的實(shí)現(xiàn)也可以是這樣的。
func getTheFlag() *int {
    return flag.Int("name", 1, "The number of greeting object.")
}

name輸出已經(jīng)是一個(gè)數(shù)字了

go run demo8.go -name=2 
Hello, 2!

我們可以用getTheFlag函數(shù)包裹(或者說包裝)那個(gè)對(duì)flag.String函數(shù)的調(diào)用,并把其結(jié)果直接作為getTheFlag函數(shù)的結(jié)果,結(jié)果的類型是*string。

這樣一來,var name =右邊的表達(dá)式,可以變?yōu)獒槍?duì)getTheFlag函數(shù)的調(diào)用表達(dá)式了。這實(shí)際上是對(duì)“聲明并賦值name變量的那行代碼”的重構(gòu)。

通常把不改變某個(gè)程序與外界的任何交互方式和規(guī)則,而只改變其內(nèi)部實(shí)現(xiàn)”的代碼修改方式,叫做對(duì)該程序的重構(gòu)。重構(gòu)的對(duì)象可以是一行代碼、一個(gè)函數(shù)、一個(gè)功能模塊,甚至一個(gè)軟件系統(tǒng)。

好了,在準(zhǔn)備工作做完之后,你會(huì)發(fā)現(xiàn),你可以隨意改變getTheFlag函數(shù)的內(nèi)部實(shí)現(xiàn),及其返回結(jié)果的類型,而不用修改main函數(shù)中的任何代碼。

這個(gè)命令源碼文件依然可以通過編譯,并且構(gòu)建和運(yùn)行也都不會(huì)有問題。也許你能感覺得到,這是一個(gè)關(guān)于程序靈活性的質(zhì)變。

我們不顯式地指定變量name的類型,使得它可以被賦予任何類型的值。也就是說,變量name的類型可以在其初始化時(shí),由其他程序動(dòng)態(tài)地確定。

在你改變getTheFlag函數(shù)的結(jié)果類型之后,Go 語言的編譯器會(huì)在你再次構(gòu)建該程序的時(shí)候,自動(dòng)地更新變量name的類型。

通過這種類型推斷,你可以體驗(yàn)到動(dòng)態(tài)類型編程語言所帶來的一部分優(yōu)勢(shì),即程序靈活性的明顯提升。但在那些編程語言中,這種提升可以說是用程序的可維護(hù)性和運(yùn)行效率換來的。

Go 語言是靜態(tài)類型的,所以一旦在初始化變量時(shí)確定了它的類型,之后就不可能再改變。這就避免了在后面維護(hù)程序時(shí)的一些問題。另外,請(qǐng)記住,這種類型的確定是在編譯期完成的,因此不會(huì)對(duì)程序的運(yùn)行效率產(chǎn)生任何影響。

總結(jié):
Go 語言的類型推斷可以明顯提升程序的靈活性,使得代碼重構(gòu)變得更加容易,同時(shí)又不會(huì)給代碼的維護(hù)帶來額外負(fù)擔(dān)(實(shí)際上,它恰恰可以避免散彈式的代碼修改),更不會(huì)損失程序的運(yùn)行效率。

4、變量的重聲明是什么意思?有什么功能?

變量聲明。通過使用它,我們可以對(duì)同一個(gè)代碼塊中的變量進(jìn)行重聲明。

說到了代碼塊,我先來解釋一下它。在 Go 語言中,代碼塊一般就是一個(gè)由花括號(hào)括起來的區(qū)域,里面可以包含表達(dá)式和語句。Go 語言本身以及我們編寫的代碼共同形成了一個(gè)非常大的代碼塊,也叫全域代碼塊。
這主要體現(xiàn)在,只要是公開的全局變量,都可以被任何代碼所使用。相對(duì)小一些的代碼塊是代碼包,一個(gè)代碼包可以包含許多子代碼包,所以這樣的代碼塊也可以很大。
接下來,每個(gè)源碼文件也都是一個(gè)代碼塊,每個(gè)函數(shù)也是一個(gè)代碼塊,每個(gè)if語句、for語句、switch語句和select語句都是一個(gè)代碼塊。甚至,switch或select語句中的case子句也都是獨(dú)立的代碼塊。走個(gè)極端,我就在main函數(shù)中寫一對(duì)緊挨著的花括號(hào)算不算一個(gè)代碼塊?當(dāng)然也算,這甚至還有個(gè)名詞,叫“空代碼塊”。

變量重聲明的前提條件如下:

由于變量的類型在其初始化時(shí)就已經(jīng)確定了,所以對(duì)它再次聲明時(shí)賦予的類型必須與其原本的類型相同,否則會(huì)產(chǎn)生編譯錯(cuò)誤。
變量的重聲明只可能發(fā)生在某一個(gè)代碼塊中。如果與當(dāng)前的變量重名的是外層代碼塊中的變量,那么就是另外一種含義了。
變量的重聲明只有在使用短變量聲明時(shí)才會(huì)發(fā)生,否則也無法通過編譯。如果要在此處聲明全新的變量,那么就應(yīng)該使用包含關(guān)鍵字var的聲明語句,但是這時(shí)就不能與同一個(gè)代碼塊中的任何變量有重名了
被“聲明并賦值”的變量必須是多個(gè),并且其中至少有一個(gè)是新的變量。這時(shí)我們才可以說對(duì)其中的舊變量進(jìn)行了重聲明。

變量重聲明其實(shí)算是一個(gè)語法糖(或者叫便利措施)。它允許我們?cè)谑褂枚套兞柯暶鲿r(shí)不用理會(huì)被賦值的多個(gè)變量中是否包含舊變量。可以想象,如果不這樣會(huì)多寫不少代碼。

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    var err error
    n, err := io.WriteString(os.Stdout, "Hello, everyone!\n") // 這里對(duì)`err`進(jìn)行了重聲明。
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    }
    fmt.Printf("%d byte(s) were written.\n", n)
}

使用短變量聲明對(duì)新變量n和舊變量err進(jìn)行了“聲明并賦值”,這時(shí)也是對(duì)后者的重聲明。

在本篇中,我們聚焦于最基本的 Go 語言程序?qū)嶓w:變量。并詳細(xì)解說了變量聲明和賦值的基本方法,及其背后的重要概念和知識(shí)。我們使用關(guān)鍵字var和短變量聲明,都可以實(shí)現(xiàn)對(duì)變量的“聲明并賦值”。

這兩種方式各有千秋,有著各自的特點(diǎn)和適用場(chǎng)景。前者可以被用在任何地方,而后者只能被用在函數(shù)或者其他更小的代碼塊中。

不過,通過前者我們無法對(duì)已有的變量進(jìn)行重聲明,也就是說它無法處理新舊變量混在一起的情況。不過它們也有一個(gè)很重要的共同點(diǎn),即:基于類型推斷,Go 語言的類型推斷只應(yīng)用在了對(duì)變量或常量的初始化方面。

5、如果一個(gè)變量與其外層代碼塊中的變量重名會(huì)出現(xiàn)什么狀況?

package main

import "fmt"

var block = "package"

func main() {
    block := "function"
    {
        block := "inner"
        fmt.Printf("The block is %s.\n", block)
    }
    fmt.Printf("The block is %s.\n", block)
}

執(zhí)行結(jié)果:

go run demo10.go 
The block is inner.
The block is function.

程序?qū)嶓w的訪問權(quán)限有三種:包級(jí)私有的、模塊級(jí)私有的和公開的,包級(jí)私有和模塊級(jí)私有訪問權(quán)限對(duì)應(yīng)的都是代碼包代碼塊,公開的訪問權(quán)限對(duì)應(yīng)的是全域代碼塊。

這個(gè)命令源碼文件中有四個(gè)代碼塊,它們是:全域代碼塊、main包代表的代碼塊、main函數(shù)代表的代碼塊,以及在main函數(shù)中的一個(gè)用花括號(hào)包起來的代碼塊。后三個(gè)代碼塊中分別聲明了一個(gè)名為block的變量,并分別把字符串值"package"、"function"和"inner"賦給了它們。此外,我在后兩個(gè)代碼塊的最后分別嘗試用fmt.Printf函數(shù)打印出“The block is %s.”。這里的“%s”只是為了占位,程序會(huì)用block變量的實(shí)際值替換掉。

首先,代碼引用變量的時(shí)候總會(huì)最優(yōu)先查找當(dāng)前代碼塊中的那個(gè)變量。注意,這里的“當(dāng)前代碼塊”僅僅是引用變量的代碼所在的那個(gè)代碼塊,并不包含任何子代碼塊。

其次,如果當(dāng)前代碼塊中沒有聲明以此為名的變量,那么程序會(huì)沿著代碼塊的嵌套關(guān)系,從直接包含當(dāng)前代碼塊的那個(gè)代碼塊開始,一層一層地查找。

一般情況下,程序會(huì)一直查到當(dāng)前代碼包代表的代碼塊。如果仍然找不到,那么 Go 語言的編譯器就會(huì)報(bào)錯(cuò)了。

從作用域的角度也可以說,雖然通過var block = "package"聲明的變量作用域是整個(gè)main代碼包,但是在main函數(shù)中,它卻被那兩個(gè)同名的變量“屏蔽”了。

雖然main函數(shù)首先聲明的block的作用域,是整個(gè)main函數(shù),但是在最內(nèi)層的那個(gè)代碼塊中,它卻是不可能被引用到的。
最內(nèi)層的{ }代碼塊會(huì)使用當(dāng)前代碼塊{ }的變量block := "inner",所以第一次打印The block is inner.。
最內(nèi)層代碼塊中的block也不可能被該塊之外的main代碼引用到,所以第二行打印“The block is function.”

6、不同代碼塊中的重名變量與變量重聲明中的變量區(qū)別到底在哪兒?

學(xué)習(xí)筆記-go程序?qū)嶓w

方便描述,把不同代碼塊中的重名變量叫做“可重名變量”。注意,在同一個(gè)代碼塊中不允許出現(xiàn)重名的變量,這違背了 Go 語言的語法。

(1)變量重聲明中的變量一定是在某一個(gè)代碼塊內(nèi)的。注意,這里的“某一個(gè)代碼塊內(nèi)”并不包含它的任何子代碼塊,否則就變成了“多個(gè)代碼塊之間”。而可重名變量指的正是在多個(gè)代碼塊之間由相同的標(biāo)識(shí)符代表的變量。
(2)變量重聲明是對(duì)同一個(gè)變量的多次聲明,這里的變量只有一個(gè)。而可重名變量中涉及的變量肯定是有多個(gè)的。
(3)不論對(duì)變量重聲明多少次,其類型必須始終一致,具體遵從它第一次被聲明時(shí)給定的類型。而可重名變量之間不存在類似的限制,它們的類型可以是任意的。
(4)如果可重名變量所在的代碼塊之間,存在直接或間接的嵌套關(guān)系,那么它們之間一定會(huì)存在“屏蔽”的現(xiàn)象。但是這種現(xiàn)象絕對(duì)不會(huì)在變量重聲明的場(chǎng)景下出現(xiàn)。

既然可重名變量的類型可以是任意的,那么當(dāng)它們之間存在“屏蔽”時(shí)你就更需要注意了。不同類型的值大都有著不同的特性和用法。當(dāng)你在某一種類型的值上施加只有在其他類型值上才能做的操作時(shí),Go 語言編譯器一定會(huì)告訴你:“這不可以”。

看個(gè)例子,兩個(gè)都叫做container的變量,分別位于main包代碼塊和main函數(shù)代碼塊。main包代碼塊中的變量是切片(slice)類型的,另一個(gè)是字典(map)類型的。在main函數(shù)的最后,我試圖打印出container變量的值中索引為1的那個(gè)元素:

package main

import "fmt"

var container = []string{"zero", "one", "two"}

func main() {
    container := map[int]string{0: "zero", 1: "one", 2: "two"}
    fmt.Printf("The element is %q.\n", container[1])
}
go run demo11.go 
The element is "one".

如果修改下代碼,把:

package main

import "fmt"

var container = []string{"zero", "one", "two"}

func main() {
    container := map[int]string{0: "zero", 1: "1", 2: "two"} //這里 修改1:"one" 為 1:"1"
    fmt.Printf("The element is %q.\n", container[1])
}

輸出是1,說明代碼執(zhí)行使用的內(nèi)層{ }代碼塊中的變量。

go run demo11.go 
The element is "1".

7、怎樣在打印其中元素之前,正確判斷變量container的類型?

答案是使用“類型斷言”表達(dá)式。具體怎么寫呢?

value, ok := interface{}(container).([]string)

學(xué)習(xí)筆記-go程序?qū)嶓w

賦值語句的賦值符號(hào)的右邊,是一個(gè)類型斷言表達(dá)式,它包括了用來把container變量的值轉(zhuǎn)換為空接口值的interface{}(container)。以及一個(gè)用于判斷前者的類型是否為切片類型 []string 的 .([]string)。

這個(gè)表達(dá)式的結(jié)果可以被賦給兩個(gè)變量,在這里由value和ok代表。變量ok是布爾(bool)類型的,它將代表類型判斷的結(jié)果,true或false。

如果是true,那么被判斷的值將會(huì)被自動(dòng)轉(zhuǎn)換為[]string類型的值,并賦給變量value,否則value將被賦予nil(即“空”)。

順便提一下,這里的ok也可以沒有。也就是說,類型斷言表達(dá)式的結(jié)果,可以只被賦給一個(gè)變量,在這里是value。但是這樣的話,當(dāng)判斷為否時(shí)就會(huì)引發(fā)異常。

類型斷言表達(dá)式的語法形式是x.(T)。其中的x代表要被判斷類型的值。這個(gè)值當(dāng)下的類型必須是接口類型的,不過具體是哪個(gè)接口類型其實(shí)是無所謂的。所以,當(dāng)這里的container變量類型不是任何的接口類型時(shí),我們就需要先把它轉(zhuǎn)成某個(gè)接口類型的值。

如果container是某個(gè)接口類型的,那么這個(gè)類型斷言表達(dá)式就可以是container.([]string)。這樣看是不是清晰一些了?

interface{}代表空接口,任何類型都是它的實(shí)現(xiàn)類型。我在下個(gè)模塊,會(huì)再講接口及其實(shí)現(xiàn)類型的問題?,F(xiàn)在你只要知道,任何類型的值都可以很方便地被轉(zhuǎn)換成空接口的值就行了。

你可能會(huì)對(duì)這里的{}產(chǎn)生疑惑,為什么在關(guān)鍵字interface的右邊還要加上這個(gè)東西?

請(qǐng)記住,一對(duì)不包裹任何東西的花括號(hào),除了可以代表空的代碼塊之外,還可以用于表示不包含任何內(nèi)容的數(shù)據(jù)結(jié)構(gòu)(或者說數(shù)據(jù)類型)。

比如你今后肯定會(huì)遇到的struct{},它就代表了不包含任何字段和方法的、空的結(jié)構(gòu)體類型。而空接口interface{}則代表了不包含任何方法定義的、空的接口類型。當(dāng)然了,對(duì)于一些集合類的數(shù)據(jù)類型來說,{}還可以用來表示其值不包含任何元素,比如空的切片值[]string{},以及空的字典值map[int]string{}。

最右邊看。圓括號(hào)中[]string是一個(gè)類型字面量。所謂類型字面量,就是用來表示數(shù)據(jù)類型本身的若干個(gè)字符。

比如,string是表示字符串類型的字面量,uint8是表示 8 位無符號(hào)整數(shù)類型的字面量。

再?gòu)?fù)雜一些的就是我們剛才提到的[]string,用來表示元素類型為string的切片類型,以及map[int]string,用來表示鍵類型為int、值類型為string的字典類型。

8、 你認(rèn)為類型轉(zhuǎn)換規(guī)則中有哪些值得注意的地方?

首先,對(duì)于整數(shù)類型值、整數(shù)常量之間的類型轉(zhuǎn)換,原則上只要源值在目標(biāo)類型的可表示范圍內(nèi)就是合法的。比如,之所以u(píng)int8(255)可以把無類型的常量255轉(zhuǎn)換為uint8類型的值,是因?yàn)?55在 [0, 255] 的范圍內(nèi)。但需要特別注意的是,源整數(shù)類型的可表示范圍較大,而目標(biāo)類型的可表示范圍較小的情況,比如把值的類型從int16轉(zhuǎn)換為int8。請(qǐng)看下面這段代碼:

var srcInt = int16(-255)
dstInt := int8(srcInt)

變量srcInt的值是int16類型的-255,而變量dstInt的值是由前者轉(zhuǎn)換而來的,類型是int8。int16類型的可表示范圍可比int8類型大了不少。
問題是,dstInt的值是多少?首先你要知道,整數(shù)在 Go 語言以及計(jì)算機(jī)中都是以補(bǔ)碼的形式存儲(chǔ)的。這主要是為了簡(jiǎn)化計(jì)算機(jī)對(duì)整數(shù)的運(yùn)算過程。補(bǔ)碼其實(shí)就是原碼各位求反再加 1。比如,int16類型的值-255的補(bǔ)碼是1111111100000001。如果我們把該值轉(zhuǎn)換為int8類型的值,那么 Go 語言會(huì)把在較高位置(或者說最左邊位置)上的 8 位二進(jìn)制數(shù)直接截掉,從而得到00000001。又由于其最左邊一位是0,表示它是個(gè)正整數(shù),以及正整數(shù)的補(bǔ)碼就等于其原碼,所以dstInt的值就是1。
一定要記住,當(dāng)整數(shù)值的類型的有效范圍由寬變窄時(shí),只需在補(bǔ)碼形式下截掉一定數(shù)量的高位二進(jìn)制數(shù)即可。
類似的快刀斬亂麻規(guī)則還有:當(dāng)把一個(gè)浮點(diǎn)數(shù)類型的值轉(zhuǎn)換為整數(shù)類型值時(shí),前者的小數(shù)部分會(huì)被全部截掉。

第二,雖然直接把一個(gè)整數(shù)值轉(zhuǎn)換為一個(gè)string類型的值是可行的,但值得關(guān)注的是,被轉(zhuǎn)換的整數(shù)值應(yīng)該可以代表一個(gè)有效的 Unicode 代碼點(diǎn),否則轉(zhuǎn)換的結(jié)果將會(huì)是"?"(僅由高亮的問號(hào)組成的字符串值)。

字符'?'的 Unicode 代碼點(diǎn)是U+FFFD。它是 Unicode 標(biāo)準(zhǔn)中定義的 Replacement Character,專用于替換那些未知的、不被認(rèn)可的以及無法展示的字符。我肯定不會(huì)去問“哪個(gè)整數(shù)值轉(zhuǎn)換后會(huì)得到哪個(gè)字符串”,這太變態(tài)了!但是我會(huì)寫下:

string(-1)

并詢問會(huì)得到什么?這可是完全不同的問題啊。由于-1肯定無法代表一個(gè)有效的 Unicode 代碼點(diǎn),所以得到的總會(huì)是"?"。在實(shí)際工作中,我們?cè)谂挪閱栴}時(shí)可能會(huì)遇到?,你需要知道這可能是由于什么引起的。

第三個(gè)知識(shí)點(diǎn)是關(guān)于string類型與各種切片類型之間的互轉(zhuǎn)的。
你先要理解的是,一個(gè)值在從string類型向[]byte類型轉(zhuǎn)換時(shí)代表著以 UTF-8 編碼的字符串會(huì)被拆分成零散、獨(dú)立的字節(jié)。除了與 ASCII 編碼兼容的那部分字符集,以 UTF-8 編碼的某個(gè)單一字節(jié)是無法代表一個(gè)字符的。

string([]byte{'\xe4', '\xbd', '\xa0', '\xe5', '\xa5', '\xbd'}) // 你好

比如,UTF-8 編碼的三個(gè)字節(jié)\xe4、\xbd和\xa0合在一起才能代表字符'你',而\xe5、\xa5和\xbd合在一起才能代表字符'好'。

其次,一個(gè)值在從string類型向[]rune類型轉(zhuǎn)換時(shí)代表著字符串會(huì)被拆分成一個(gè)個(gè) Unicode 字符。

string([]rune{'\u4F60', '\u597D'}) // 你好

當(dāng)你真正理解了 Unicode 標(biāo)準(zhǔn)及其字符集和編碼方案之后,上面這些內(nèi)容就會(huì)顯得很容易了。什么是 Unicode 標(biāo)準(zhǔn)?我會(huì)首先推薦你去它的http://www.unicode.org/ 官方網(wǎng)站一探究竟。

9、什么是別名類型?什么是潛在類型?

我們可以用關(guān)鍵字type聲明自定義的各種類型。當(dāng)然了,這些類型必須在 Go 語言基本類型和高級(jí)類型的范疇之內(nèi)。在它們當(dāng)中,有一種被叫做“別名類型”的類型。我們可以像下面這樣聲明它:

type MyString = string

這條聲明語句表示,MyString是string類型的別名類型。顧名思義,別名類型與其源類型的區(qū)別恐怕只是在名稱上,它們是完全相同的。源類型與別名類型是一對(duì)概念,是兩個(gè)對(duì)立的稱呼。別名類型主要是為了代碼重構(gòu)而存在的

Go 語言內(nèi)建的基本類型中就存在兩個(gè)別名類型。byte是uint8的別名類型,而rune是int32的別名類型。

一定要注意,如果我這樣聲明:

type MyString2 string // 注意,這里沒有等號(hào)。

MyString2和string就是兩個(gè)不同的類型了。這里的MyString2是一個(gè)新的類型,不同于其他任何類型。這種方式也可以被叫做對(duì)類型的再定義。我們剛剛把string類型再定義成了另外一個(gè)類型MyString2。
學(xué)習(xí)筆記-go程序?qū)嶓w
對(duì)于這里的類型再定義來說,string可以被稱為MyString2的潛在類型。潛在類型的含義是,某個(gè)類型在本質(zhì)上是哪個(gè)類型。
潛在類型相同的不同類型的值之間是可以進(jìn)行類型轉(zhuǎn)換的。因此,MyString2類型的值與string類型的值可以使用類型轉(zhuǎn)換表達(dá)式進(jìn)行互轉(zhuǎn)。

但對(duì)于集合類的類型[]MyString2與[]string來說這樣做卻是不合法的,因?yàn)閇]MyString2與[]string的潛在類型不同,分別是[]MyString2和[]string。另外,即使兩個(gè)不同類型的潛在類型相同,它們的值之間也不能進(jìn)行判等或比較,它們的變量之間也不能賦值。

package main

import (
    "fmt"
)

var container = []string{"zero", "one", "two"}

func main() {
    container := map[int]string{0: "zero", 1: "one", 2: "two"}

    // 方式1。
    _, ok1 := interface{}(container).([]string)
    _, ok2 := interface{}(container).(map[int]string)
    if !(ok1 || ok2) {
        fmt.Printf("Error: unsupported container type: %T\n", container)
        return
    }
    fmt.Printf("The element is %q. (container type: %T)\n",
        container[1], container)

    // 方式2。
    elem, err := getElement(container)
    if err != nil {
        fmt.Printf("Error: %s\n", err)
        return
    }
    fmt.Printf("The element is %q. (container type: %T)\n",
        elem, container)
}

func getElement(containerI interface{}) (elem string, err error) {
    switch t := containerI.(type) {
    case []string:
        elem = t[1]
    case map[int]string:
        elem = t[1] //這里如果改為t[0],輸出是zero
    default:
        err = fmt.Errorf("unsupported container type: %T", containerI)
        return
    }
    return
}
 go run demo12.go 
The element is "one". (container type: map[int]string)
The element is "one". (container type: map[int]string)
package main

import (
    "fmt"
)

func main() {
    // 重點(diǎn)1的示例。
    var srcInt = int16(-255)
    // 請(qǐng)注意,之所以要執(zhí)行uint16(srcInt),是因?yàn)橹挥羞@樣才能得到全二進(jìn)制的表示。
    // 例如,fmt.Printf("%b", srcInt)將打印出"-11111111",后者是負(fù)數(shù)符號(hào)再加上srcInt的絕對(duì)值的補(bǔ)碼。
    // 而fmt.Printf("%b", uint16(srcInt))才會(huì)打印出srcInt原值的補(bǔ)碼"1111111100000001"。
    fmt.Printf("The complement of srcInt: %b (%b)\n",
        uint16(srcInt), srcInt)
    dstInt := int8(srcInt)
    fmt.Printf("The complement of dstInt: %b (%b)\n",
        uint8(dstInt), dstInt)
    fmt.Printf("The value of dstInt: %d\n", dstInt)
    fmt.Println()

    // 重點(diǎn)2的示例。
    fmt.Printf("The Replacement Character: %s\n", string(-1))
    fmt.Printf("The Unicode codepoint of Replacement Character: %U\n", '?')
    fmt.Println()

    // 重點(diǎn)3的示例。
    srcStr := "你好"
    fmt.Printf("The string: %q\n", srcStr)
    fmt.Printf("The hex of %q: %x\n", srcStr, srcStr)
    fmt.Printf("The byte slice of %q: % x\n", srcStr, []byte(srcStr))
    fmt.Printf("The string: %q\n", string([]byte{'\xe4', '\xbd', '\xa0', '\xe5', '\xa5', '\xbd'}))
    fmt.Printf("The rune slice of %q: %U\n", srcStr, []rune(srcStr))
    fmt.Printf("The string: %q\n", string([]rune{'\u4F60', '\u597D'}))
}
go run demo13.go 
The complement of srcInt: 1111111100000001 (-11111111)
The complement of dstInt: 1 (1)
The value of dstInt: 1

The Replacement Character: ?
The Unicode codepoint of Replacement Character: U+FFFD

The string: "你好"
The hex of "你好": e4bda0e5a5bd
The byte slice of "你好": e4 bd a0 e5 a5 bd
The string: "你好"
The rune slice of "你好": [U+4F60 U+597D]
The string: "你好"
package main

import "fmt"

func main() {
    // 示例1。
    {
        type MyString = string
        str := "BCD"
        myStr1 := MyString(str)
        myStr2 := MyString("A" + str)
        fmt.Printf("%T(%q) == %T(%q): %v\n",
            str, str, myStr1, myStr1, str == myStr1)
        fmt.Printf("%T(%q) > %T(%q): %v\n",
            str, str, myStr2, myStr2, str > myStr2)
        fmt.Printf("Type %T is the same as type %T.\n", myStr1, str)

        strs := []string{"E", "F", "G"}
        myStrs := []MyString(strs)
        fmt.Printf("A value of type []MyString: %T(%q)\n",
            myStrs, myStrs)
        fmt.Printf("Type %T is the same as type %T.\n", myStrs, strs)
        fmt.Println()
    }
    // 示例2。
    {
        type MyString string
        str := "BCD"
        myStr1 := MyString(str)
        myStr2 := MyString("A" + str)
        _ = myStr2
        //fmt.Printf("%T(%q) == %T(%q): %v\n",
        //  str, str, myStr1, myStr1, str == myStr1) // 這里的判等不合法,會(huì)引發(fā)編譯錯(cuò)誤。
        //fmt.Printf("%T(%q) > %T(%q): %v\n",
        //  str, str, myStr2, myStr2, str > myStr2) // 這里的比較不合法,會(huì)引發(fā)編譯錯(cuò)誤。
        fmt.Printf("Type %T is different from type %T.\n", myStr1, str)

        strs := []string{"E", "F", "G"}
        var myStrs []MyString
        //myStrs := []MyString(strs) // 這里的類型轉(zhuǎn)換不合法,會(huì)引發(fā)編譯錯(cuò)誤。
        //fmt.Printf("A value of type []MyString: %T(%q)\n",
        //  myStrs, myStrs)
        fmt.Printf("Type %T is different from type %T.\n", myStrs, strs)
        fmt.Println()
    }
    // 示例3。
    {
        type MyString1 = string
        type MyString2 string
        str := "BCD"
        myStr1 := MyString1(str)
        myStr2 := MyString2(str)
        myStr1 = MyString1(myStr2)
        myStr2 = MyString2(myStr1)

        myStr1 = str
        //myStr2 = str // 這里的賦值不合法,會(huì)引發(fā)編譯錯(cuò)誤。
        //myStr1 = myStr2 // 這里的賦值不合法,會(huì)引發(fā)編譯錯(cuò)誤。
        //myStr2 = myStr1 // 這里的賦值不合法,會(huì)引發(fā)編譯錯(cuò)誤。
    }
}
 go run demo14.go 
string("BCD") == string("BCD"): true
string("BCD") > string("ABCD"): true
Type string is the same as type string.
A value of type []MyString: []string(["E" "F" "G"])
Type []string is the same as type []string.

Type main.MyString is different from type string.
Type []main.MyString is different from type []string.

分享題目:學(xué)習(xí)筆記-go程序?qū)嶓w
文章鏈接:http://weahome.cn/article/jhijde.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部