互聯網世界中的C語言——我的golang學習筆記:1(基礎語法快速過)


前言

學習任何知識都會有一個學習背景

最近,我們團隊乃至我司整個雲服務,上go的呼聲越來越高!新服務已經開始用go開發,部分現有Java版的服務重構為go也只是時間問題而已,故相關技術積累勢在必行!在雲網絡的分布式服務乃至在一切高並發,分布式后台服務中,golang都有着很大的優勢。

據我對國內互聯網行業的實際考察,了解,目前國內主流互聯網公司都在積極投入go的懷抱……

青雲更是全棧使用了go……

還有火的一塌糊塗的docker。

它為雲而生。

它為並發而生。

還有go的安全、簡潔、高效

有良好的Java、C/C++背景,拿起go很容易。

……

參考:

2017年值得學習的三大編程語言

2017年編程語言排行榜,相比2016,go的飛速上升趨勢很明顯。而Java有很大份額的縮減……

當然,當前在企業級應用開發中,Java依然占有一席之地。

而個人認為:一個優秀的研發工程師乃至架構師……是不能僅僅局限於一門語言的,語言只是拿來就用的工具,關鍵還是實戰經驗和行業內功的修煉。但是對於任何編程語言,不學也不是憑空就會的。

簡單說go的歷史和應用

歷史和概況

Go語言是一個年輕的語言,是Google在2009年發布的第二款開源的編程語言,它是編譯型語言,專門針對多處理器系統應用程序的編程進行了優化,因此使用Go 編譯的程序可以媲美C或C++代碼的速度。
go還具有類似Java的垃圾回收機制,且實現了數組安全……故go有機的結合了Java和C/C++,使其本身更加安全、高效的同時還天然支持並行進程。
可以說,go是為並發而生的! 既然它是為並發而生,那么在編寫高並發程序時,go寫起來也十分簡潔。

目前流行的go應用

1、Go語言的殺手級應用就是Docker,Docker應該大家都知道,目前在國內火的一塌糊塗
2、Codis,一種Redis的集權解決管理方案,很大部分go開發,由豆瓣推出。
3、
Glow,類似Hadoop,也是一種大數據處理框架,性能非常好,是Hadoop的go的實現。
 
4、
Cockroach數據庫,譯作蟑螂……意味着該數據庫的生存能力很強,是高穩定性業務環境的首選數據庫之一
 
……

go的開發環境搭建

可以選擇源碼安裝、標准安裝包(win下),第三方工具安裝(brew,install,yum等)。
如果是win,就使用標准包。官網有下載地址。
推薦第三方工具安裝,簡單。
比如Mac下的Homerbrew
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安裝go

brew install go

看安裝成功之后的提示就能知道,go的運行需要一個環境變量——GOPATH,是go中非常重要的一個環境變量,必須配置好,假如不配,那么安裝go的一些工具,或者自己寫的包,就會出問題。當然還有GOROOT。

PS:其實在目前的go版本中,這些環境變量已經在安裝的時候默認被配置ok了。只需要在機器的環境配置文件中做如下操作即可:

You may wish to add the GOROOT-based install location to your PATH:
  export PATH=$PATH:/usr/local/opt/go/libexec/bin
==> Summary
🍺  /usr/local/Cellar/go/1.9: 7,639 files, 293.7MB
$GOPATH 目錄約定有三個子目錄:
  • src 存放源代碼(比如:.go .c .h .s,以及下載安裝的一些工具包)
  • pkg 編譯后生成的文件(比如:.a)
  • bin 編譯后生成的可執行文件(為了方便,可以把此目錄加入到 $PATH 變量中)

下面查看go的環境變量配置,看看配置的情況如何了。

發現查看環境變量的是go env命令

bash-3.2$ go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/dashuai/go"
GORACE=""
GOROOT="/usr/local/Cellar/go/1.9/libexec"
GOTOOLDIR="/usr/local/Cellar/go/1.9/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build746511617=/tmp/go-build -gno-record-gcc-switches -fno-common"
CXX="clang++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"

發現GOROOT、GOPATH等環境變量確實已經在安裝go 1.9 時,被默認設置。

然后,按照之前安裝go成功之后的提示,在.bash_profile文件中進行如下配置:

 export PATH=$PATH:/usr/local/opt/go/libexec/bin

最后,source下即可。

PS:各個環境變量含義

  • $GOROOT 表示go的安裝位置,它的值一般都是 $HOME/go,當然也可以安裝在別的地方,這個都無所謂的。
  • $GOARCH 表示目標機器的處理器架構。
  • $GOOS 表示目標機器的操作系統,它的值可以是 darwin,freebsd,linux 或 windows。
  • $GOBIN 表示編譯器和鏈接器的安裝位置,默認是 $GOROOT/bin,如果使用的是 Go 1.0.3 及以后的版本,一般情況下可以將它的值設置為空,Go 將會使用前面提到的默認值。
  • $GOPATH 表示工作的環境路徑,允許包含多個目錄。當有多個目錄時,Windows分隔符是分號,Linux是冒號,當有多個GOPATH時,默認會將go get命令的內容放在第一個目錄下。$GOPATH 目錄約定有三個子目錄:
    • src 存放源代碼(比如:.go .c .h .s 等)
    • pkg 編譯后生成的文件(比如:.a)
    • bin 編譯后生成的可執行文件(為了方便,可以把此目錄加入到 \$PATH 變量中,如果有多個gopath,那么使用${GOPATH//://bin:}/bin添加所有的bin目錄)很多Go 命令都依賴於此變量,例如go get命令會將獲取到的包放到GOPATH中。

go的IDE

可以使用LiteIDE,為go量身打造。
熟悉Java的也可以直接使用eclipse或者intelli idea,都有相關插件,本人使用了eclipse。
當然,直接上VIM也是大有人在的。

hello word!

和Java一樣,go定義包使用package,導入包使用import,程序入口也是main,函數聲明使用func,同Java,Go也需要括號包住函數體,不同的是,語句的結束可以加分號,也可以不加,看個人習慣。比如從Java,C++來的,又是強迫症患者,那么一般都要寫分號。如下:
  1 package main
  2 import "fmt"
  3
  4 /*
  5 注釋風格和C/C++,JAVA類似
  6 */
  7 func main() {
  8     fmt.Println("hello world!"); //打印hello world!
  9 }
 10
運行go程序,可以使用build命令先編譯連接源代碼,生成可執行文件(程序文件名本身),方便以后隨時拿來運行。 
wangyishuaideMacBook-Pro:goStu wangyishuai$ go build hello.go
wangyishuaideMacBook-Pro:goStu wangyishuai$ ls
hello    hello.go
wangyishuaideMacBook-Pro:goStu wangyishuai$ ./hello
hello world!

當然,圖省事,直接使用go run命令執行也是ok的,但是這樣不會生成任何中間結果。

wangyishuaideMacBook-Pro:goStu wangyishuai$ rm hello
wangyishuaideMacBook-Pro:goStu wangyishuai$ ls
hello.go
wangyishuaideMacBook-Pro:goStu wangyishuai$ go run hello.go
hello world!
wangyishuaideMacBook-Pro:goStu wangyishuai$ ls
hello.go

有一個小坑,如果import的包不用,編譯會報錯。比如,fmt包導入了就必須使用,類似的包括變量等。當注釋掉第8行代碼,在執行會報錯,這點嚴謹性,比Java強多了。

wangyishuaideMacBook-Pro:goStu wangyishuai$ go run hello.go
# command-line-arguments
./hello.go:2:8: imported and not used: "fmt"

go的常用關鍵字(25個)

  • var和const :變量和常量的聲明
  • var varName type  或者 varName : = value
  • package and import: 導入
  • func: 用於定義函數和方法
  • return :用於從函數返回
  • defer someCode :在函數退出之前執行
  • go : 用於並行
  • select 用於選擇不同類型的通訊
  • interface 用於定義接口
  • struct 用於定義抽象數據類型
  • break、case、continue、for、fallthrough、else、if、switch、goto、default 流程控制
  • chan用於channel通訊
  • type用於聲明自定義類型
  • map用於聲明map類型數據
  • range用於讀取slice、map、channel數據

如上,其實和其他大部分語言都差不多。其中go、chan、以及select是go的特色和重點,又比如map類似Java的hashmap……其他的也沒什么新鮮的,接下來快速過。

go程序的組織模式

go程序類似Java,都是通過package組織,然后每個獨立的、可運行的go程序的入口必須都是main函數,也就是main包里的main函數。

變量和常量

使用var關鍵字

var是go最基本的定義變量方式 ,但是它的變量類型是放在了變量名后面。

如下,格式化打印語句

Printf

和C語言非常像,其實go就是C語言衍生改進而來,這也是為什么稱它為互聯網世界中的C語言的原因。

package main 

import (
	"fmt"
)

func main() {
	var x int;
	x = 1;
	fmt.Printf("%d", x);
}

簡潔的定義

有時候會覺得總是多寫一個var比較煩,那么可以用:=來定義變量(個人感覺也不是想象中的那么簡潔……)

package main 

import (
    "fmt"
)

func main() {
    var x int;
    x = 1;
    fmt.Printf("%d", x);
    
    a, b, c := 1, 2, 3; // 自動判斷類型,類似Python
    fmt.Println(a, b, c);
}
View Code

這種方式類似Python,省去了寫類型的步驟,在go程序中十分常見。但是這樣的寫法有一個缺點::= 無法用於函數體外部,而var可以。故var也是有存在的必要的。

eclipse直接提示變量y的定義錯誤,而變量x是ok的。

注意:

1、package和import必須挨着,也就是它們中間不能有其他代碼,否則報錯。

2、對於沒有顯式初始化的變量,Go語言總是將零值賦值給該變量。

3、在Go語言中,聲明變量的時候,類型名總是在變量名的后面。

const常量

沒什么可說的,和C語言一樣的用法,唯一不同的就是定義常量時,加類型或者不加都可以,但是注意,最好養成習慣,類型加在常量名后面。

func main() {    
    fmt.Println(x);
    const dashuai string = "dashuai";
    const gogogo = 888;
}
View Code

而且,字符串數據類型和Java不太一樣,是完全小寫的string。下面會說到。

注意:

1、當需要設置多個常量的時候,不必重復使用const,可以使用小括號設置 (var同理)

package main

import (
    "fmt"
)

func main() {
    var (
        a int;
        b string;
        c bool;
    )
    
    fmt.Println(a, b, c) // 0  false
    
    const (
        aa = 1
        bb = "dada"
        cc = true
    )
    
    fmt.Println(aa, bb, cc) 
}
View Code

數據類型

bool byte complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr

無類型的數值常量可以兼容go內置的任何類型的數值

在不同類型數值之間進行運算或者比較操作,需要進行類型轉換。類型轉換采用type(value)的方式,只要合法就能轉換成功,即使會導致數據精度丟失。

快速過一下,和Java對比,如下常用的幾個:

整型

1、go獨有的 rune,其實就是代表了無符號32位整型 unit32。

2、有符號整型 int (依賴於os,可能是int32或者int64)

  1. int8 [-128, 127]
  2. int16 [-32768, 32767]
  3. int32 [-2147483648, 2147483647]
  4. int64 [-9223372036854775808, 9223372036854775807]

也就是說,go是見名知意,go中可以直接定義xxx位的整型。

3、字節 byte,等價於 uint8

4、無符號整型 uint ,依賴於不同os實現,可以是uint32或者uint64

  1. uint8 [0, 255]
  2. uint16 [0, 65535]
  3. uint32 [0, 4294967295]
  4. uint64 [0, 18446744073709551615]
  5. uintptr 一個可以恰好容納指針值的無符號整型(對32位平台是uint32, 對64位平台是uint64)

可以通過unsafe.Sizeof函數實現類似C語言里sizeof操作符的功能,查看類型的字節長度。

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    str := "dashuai"
    fmt.Println(unsafe.Sizeof(str)) // 16

    a := 1
    fmt.Println(unsafe.Sizeof(a)) // 8

    var b int8 = 1
    fmt.Println(unsafe.Sizeof(b)) // 1

    var c int32 = 1
    fmt.Println(unsafe.Sizeof(c)) // 4

    var d int64 = 1
    fmt.Println(unsafe.Sizeof(d)) // 8
}
View Code

unsafe包含了用於獲取Go語言類型信息的方法。

浮點類型

1、float32 ,±3.402 823 466 385 288 598 117 041 834 845 169 254 40x1038 計算精度大概是小數點后7個十進制數

2、float64,±1.797 693 134 862 315 708 145 274 237 317 043 567 981x1038 計算精度大概是小數點后15個十進制數

布爾類型 bool(取值true false)

注意區分Java的boolean/Boolean寫法,其實是和C語言一樣,這里也說下,學習go,就對比着C語言和Java語言,這樣用起來是很快的。

布爾值使用內置的true和false。Go語言支持標准的邏輯和比較操作,這些操作的結果都是布爾值。還可以通過!b的方式反轉變量b的真假。也就是邏輯非。

go可直接定義復數

這也是go獨有的, complex32、complex64,在針對一些算法的實現時,非常方便,比如傅里葉變換。

func main() {    
    var x complex64 = 10 + 9i;
    fmt.Print(x + x);    
}
View Code

complex32復數,實部和虛部都是float32

complex64復數,實部和虛部都是float64

字符串

常用是string定義,Go語言中的字符串是 UTF-8編碼格式(當字符為 ASCII 碼時則占用 1 個字節,其它字符根據需要占用 2-4 個字節)。

UTF-8 是被廣泛使用的編碼格式,是文本文件的標准編碼,其它包括 XML 和 JSON 在內,也都使用該編碼。由於該編碼對占用字節長度的不定性,Go 中的字符串也可能根據需要占用 1 至 4 個字節,這與C++、Java 或者 Python 不同。

Go 這樣做的好處是:不僅減少了內存和硬盤空間占用,同時也不用像其它語言那樣需要對使用 UTF-8 字符集的文本進行編碼和解碼。

Go語言中字符串的可以使用雙引號( " )或者反引號( ` )來創建。

雙引號用來創建可解析的字符串,所謂可解析的是指字符串中的一些符號可以被格式化為其他內容,如 "\n" 在輸出時候會被格式化成換行符, 如果需要按照原始字符輸出必須進行轉義。

反引號創建的字符串原始是什么樣,那輸出還是什么,不需要進行任何轉義。

package main

import (
    "fmt"
)

func main() {
    str := "123"
    str1 := "\n"
    str2 := `\n`
    str3 := "\\n"
    fmt.Println(str) // 123
    fmt.Println(str1) // 換行
    fmt.Println(str2) // \n
    fmt.Println(str3) // \n 
}
View Code

注意

1、Go語言中的部分轉義字符
\\ 反斜線

\'  單引號

\" 雙引號

\n 換行符

\uhhhh  4個16進制數字給定的Unicode字符

2、在Go語言中單個字符可以使用單引號 ' 來創建。而且,一個單一的字符可以用一個單一的rune來表示(rune是uint32類型)。這也是容易理解的,因為Go語言的字符串是UTF-8編碼,其底層使用4個字節表示,也就是32 bit。

格式化字符串

目前為止,使用了fmt.Printffmt.Prinfln,對於前者的使用,就像C語言中的printf函數一樣,常用的格式化指令如下:

格式化指令 含義
%% %字面量
%b 一個二進制整數,將一個整數格式化為二進制的表達方式
%c 一個Unicode的字符
%d 十進制數值
%o 八進制數值
%x 小寫的十六進制數值
%X 大寫的十六進制數值
%U 一個Unicode表示法表示的整形碼值,默認是4個數字字符
%s 輸出以原生的UTF-8字節表示的字符,如果console不支持UTF-8編碼,則會輸出亂碼
%t 以true或者false的方式輸出布爾值
%v 使用默認格式輸出值,或者使用類型的String()方法輸出的自定義值,如果該方法存在的話
%T 輸出值的類型

常用的格式化指令修飾符如下:

  • 空白 如果輸出的數字為負,則在其前面加上一個減號"-"。如果輸出的是整數,則在前面加一個空格。使用%x或者%X格式化指令輸出時,會在結果之間添加一個空格。例如fmt.Printf("% X", "實")輸出E5 AE 9E
  • #
    • %#o 輸出以0開始的八進制數據
    • %#x 輸出以0x開始的十六進制數據
  • + 讓格式化指令在數值前面輸出+號或者-號,為字符串輸出ASCII字符(非ASCII字符會被轉義),為結構體輸出其字段名
  • - 讓格式化指令將值向左對齊(默認值為像右對齊)
  • 0 讓格式指令以數字0而非空白進行填充

字符串處理包

strings 包提供了如查找字符串,分割字符串,判斷前后綴,判斷字符串包含,字符串替換,統計字符串出現的次數等常用操作。

strconv 包提供了許多可以在字符串和其他類型的數據之間進行轉換的函數。例如可以將數字轉換為字符串,將數字樣式的字符串轉換為數值(將字符串"12345"轉換int類型的整數)

這些直接查文檔就可以了。

數組 array slice

array可以類比Python的元組,slice可以類比Python的列表,Java的list,很好理解。

package main 

import (
    "fmt"
)

/*
array用法
*/
func main() {    
    var x [10]int;
    x[0] = 100;
    x[8] = 800;
    // %v 可以直接遍歷打印數組,非常方便,C語言是沒有的。非常高級的用法%v,意思是value,同樣數組也不能越界使用。
    // 如果沒有初值,自動設為0
    fmt.Printf("%v \n", x);    
    // 看長度,和Python的len函數一樣
    fmt.Println(len(x));
    
    var str [5]string;
    str[3] = "aaaa";
    // 默認初值為空串
    fmt.Printf("%v \n", str);
    
    // 如果使用簡潔聲明,那么可以直接賦值
    y := [3]int{1, 2, 3}
    fmt.Printf("%v \n", y);
}
View Code

在看下slice,其實它就是一個動態數組

    var x [10]int; // 這是一個靜態數組,array,之前說過了
    var slice_x []int; // 非常明顯,不需要顯式聲明長度

還可以通過從一個數組或在一個已經存在的slice中再次聲明slice

    xx := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 一個靜態數組
    z := xx[1:3]; //對xx切片(和Python一樣的道理),切片范圍是前閉后開區間,也就是截取了2,3。然后截取的結果賦值給數組z,go的數組同樣從下標0開始

其實,現在的z就變為了動態數組slice,類似於指針。

同樣slice也可以用簡潔方式聲明,並且配合關鍵字make去分配內存空間,make用於內建類型(map、slice 和channel)的內存分配,有些類似C語言的malloc。

package main 

import (
    "fmt"
)

func main() {
    x := make([]int, 3, 8); // 長度為3,容量為8
    fmt.Printf("%v \n", x); // [0 0 0] 
    
    // 會自動擴容
    x = append(x, 1, 2, 3, 4, 5, 6, 7, 8);
    fmt.Printf("%v \n", x); // [0 0 0 1 2 3 4 5 6 7 8] 
    fmt.Printf("%v %v \n", len(x), cap(x)); // 11 16 
}
View Code

小結

1、總的來說,如下圖,可以從一個array聲明slice,其實就是指針指向

2、在Go語言中,字符串支持切片操作,但是需要注意的是:

如果字符串都是由ASCII字符組成,那可以隨便使用切片進行操作

如果字符串中包含其他非ASCII字符,直接使用切片獲取想要的單個字符時需要十分小心,因為對字符串直接使用切片時是通過字節進行索引的,但是非ASCII字符在內存中可能不是由一個字節組成。如果想對字符串中字符依次訪問,可以使用range操作符。

另外獲取字符串的長度可能有兩種含義,一種是指獲取字符串的字節長度,一種是指獲取字符串的字符數量。

3、字符串支持以下操作:

語法 描述
s += t 將字符串t追加到s末尾
s + t 將字符串s和t級聯
s[n] 從字符串s中索引位置為n處的原始字節
s[n:m] 從位置n到位置m-1處取得的字符(字節)串
s[n:] 從位置n到位置len(s)-1處取得的字符(字節)串
s[:m] 從位置0到位置m-1處取得的字符(字節)串
len(s) 字符串s中的字節數
len([]rune(s)) 字符串s中字符的個數,可以使用更快的方法utf8.RuneCountInString()
[ ]rune(s) 將字符串s轉換為一個unicode值組成的串
string(chars) chars類型是[]rune或者[]int32, 將之轉換為字符串
[ ]byte(s) 無副本的將字符串s轉換為一個原始的字節的切片數組,不保證轉換的字節是合法的UTF-8編碼字節

指針

通常情況下Go語言中的變量持有相應的值。也就是說,我們可以將一個變量想象成它所持有的值來使用。

其中有些例外:通道、函數、方法、map、切片slice 是引用變量,它們持有的都是引用,也即保存指針的變量。

在go中,值在傳遞給函數或者方法的時候會被復制一次,對於布爾類型和數值類型來說這so easy,但是對於大型變量卻代價很大。而且復制值傳參的方式,修改值只是修改了副本,這能保證原始變量不被修改,但也一定程度上增加了修改原始值的麻煩。(和C語言一樣的道理,同樣go也保留了C語言的指針)

Go語言中有指針,也沒什么新鮮東西,同樣在每次傳遞給函數或者方法的只是變量的內存地址,這是非常高效的。而且一個被指針指向的變量可以通過該指針來修改,這就很方便的在函數或者方法中通過指針修改原始變量。

Go語言中的指針操作符也是使用&*操作符。

交換兩個變量

func swap1(x, y, p *int) {
    if *x > *y {
        *x, *y = *y, *x
    }

    *p = *x + *y
}
View Code

map

類比Java的hashmap,在go中,常用於json解碼等。map和Java的哈希表一樣,必須申請內存,使用make,否則報錯

    var a map[string]int32;
    a["aaaa"] = 111;
    fmt.Printf("%v \n", a); 
View Code

執行出錯,nil就是null,零值。

panic: assignment to entry in nil map

map和slice不一樣,slice可以不make直接用,底層會做處理,但是map不行。

    var a map[string]int32;
    a = make(map[string]int32);
    a["111"] = 111;
    
    fmt.Printf("%v \n", a); // map[111:111] 
View Code

還是使用簡潔寫法爽,個人也推薦

    a := make(map[string]int32);

流程控制

這個東西幾乎每個語言都有,下面看常用的幾個

if 條件

package main 

import (
    "fmt"
)

func main() {
    x := 1;
    if x > 1 { // 注意條件表達式沒有括號包住()
        fmt.Println(x);     
    } else if  x < 1 {
        fmt.Println(x);     
    } else {
        fmt.Println(x);     
    }
}
View Code

for 循環語句,兩種用法

1、當做普通的for循環

package main 

import (
    "fmt"
)

func main() {
    var x int;
    sum := 0;
    
    for x = 1; x <= 10; x++ {
        sum += x;
    }
    
    fmt.Println(sum);
}
View Code

2、當做while循環使用(因為go里沒有while關鍵字) 

package main 

import (
    "fmt"
)

func main() {
    var x int = 1;
    sum := 0;
    
    for x <= 100 {
        sum += x;
        x++;
    }
    
    fmt.Println(sum);
}
View Code

注意

:=操作符用於多個逗號分隔的變量時,如果該變量已經存在,則只是簡單的修改它的值。但是當:=操作符位於作用域的起始處時,Go語言會創建一個新的變量,不管該變量之前是否存在,如在if或者for語句中。

a, b, c := 2, 3, 5 
for a := 7; a < 8; a++ {
    fmt.Println(a)
}
View Code

例子先使用:=聲明並賦值了三個變量。for語句處又一次使用:=操作符聲明了變量a。而for語句代表了一個新的作用域,所以:=在這里新聲明創建了一個變量a,這個變量和之前的變量a是完全不同的兩個變量(內存地址不一樣)

switch case 

package main 

import (
    "fmt"
)

func main() {
    var x int = 1;
    
    switch x { // 同樣沒有括號
        case 1:
            fmt.Println(1); // 不需要break,go自身做了優化,默認給加了break語句。但是也可以手動加上。
        case 2:
            fmt.Println(2);
        default:
            fmt.Println(3);
    }
}
View Code

如果想讓switch語句在case執行后不中斷,繼續執行接下來的流程,可以使用fallthrough(穿透語句)

package main 

import (
    "fmt"
)

func main() {
    var x int = 1;
    
    switch x { // 同樣沒有括號
        case 1:
            fmt.Println(1); // 不需要break,go自身做了優化,默認給加了break語句。但是也可以手動加上。
            fallthrough;
        case 2:
            fmt.Println(2);
        default:
            fmt.Println(3);
    }
} // 打印 1 換行 2
View Code

很重要的一個關鍵字 range

類似Java的迭代器,可以便捷的遍歷容器

package main

import (
    "fmt"
)

func main() {
    x := [5]int{1, 2, 3, 4, 5}

    for i, v := range x {
        fmt.Println(i, v) // i相當於key,v相當於value,和Java的foreach很像
    }

    // 當然,可以不用關心key,只取value,go提供了一個符號 _,代表丟棄值
    for _, v := range x {
        fmt.Println(v)
    }
/*
1
2
3
4
5
*/

    // 還可以遍歷字符串,默認打印的是ask碼
    str := "abcdefg"
    for i, v := range str {
        fmt.Println(i, v)
    }
/*
0 97
1 98
2 99
3 100
4 101
5 102
6 103
*/

    // 如果想打印字符串
    for _, v := range str {
        fmt.Printf("%c \n", v)
    }
    /*
a 
b 
c 
d 
e 
f 
g 
    */
}
View Code

另外, "_" 也叫空標示符,它是一個占位符,用於在賦值操作的時候將某個值賦值給空標示符號,從而達到丟棄該值的目的。空標示符不是一個新的變量,因此將它用於:= 操作符號的時候,必須同時為至少另一個值賦值。

 

歡迎關注

dashuai的博客是終身學習踐行者,大廠程序員,且專注於工作經驗、學習筆記的分享和日常吐槽,包括但不限於互聯網行業,附帶分享一些PDF電子書,資料,幫忙內推,歡迎拍磚!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM