這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
[TOC]
一.變數
1.聲明變數
變數相當於是對一塊資料存放區空間的命名,程式可以通過定義一個變數來申請一塊資料存放區空間,之後可以通過引用變數名來使用這Block Storage空間。
Go語言引入了關鍵字var,而類型資訊放在變數名之後,變數聲明語句不需要使用分號作為結束符,樣本如下:var v1 intvar v2 stringvar v3 [10]int // 數組var v4 []int // 數組切片var v5 struct {f int}var v6 *int // 指標var v7 map[string]int // map, key為string類型, value為int類型var v8 func(a int) int
var關鍵字的另一種用法是可以將若干個需要聲明的變數放置在一起,免得程式員需要重複
寫var關鍵字,如下所示:
var (v1 intv2 string)
對於聲明變數時需要進行初始化的情境, var關鍵字可以保留,但不再是必要的元素:
var v1 int = 10 // 正確的使用方式1var v2 = 10 // 正確的使用方式2,編譯器可以自動推匯出v2的類型v3 := 10 // 正確的使用方式3,編譯器可以自動推匯出v3的類型
冒號和等號的組合:=,用於明確表達同時進行變數聲明和初始化的工作。
不能重複聲明:
var i inti := 2會導致類似如下的編譯錯誤:no new variables on left side of :=
2.匿名變數
_是特殊標識符,用來忽略結果
package mainimport( "fmt")func cal(a int, b int)(int,int) { sum := a + b avg := (a+b)/2 return sum, avg}func main() { _,avg := cal(100,200) fmt.Println(avg)}
二、常量
在Go語言中,常量是指編譯期間就已知且不可改變的值。常量可以是數實值型別(包括整型、浮點型和複數類型)、布爾類型、字串類型等。
const Pi float64 = 3.14159265358979323846const zero = 0.0 // 無類型浮點常量const ( size int64 = 1024 eof = -1 // 無類型整型常量)const u, v float32 = 0, 3 // u = 0.0, v = 3.0,常量的多重賦值const a, b, c = 3, 4, "foo"// a = 3, b = 4, c = "foo", 無類型整型和字串常量
定義兩個常量a=1和b=2,擷取目前時間的秒數,如果能被b整除,則在終端列印b,否則列印a。
package mainimport( "fmt" "time")const ( a = 1 b = 2)func main() { for { second := time.Now().Unix() fmt.Print(second," ") if (second % b == 0) { fmt.Println("b") } else { fmt.Println("a") } time.Sleep(1000 * time.Millisecond) }}
三、資料類型
1.整型
類 型 長度(位元組) 值 範 圍int8 1 -128 ~ 127uint8(即byte) 1 0 ~ 255int16 2 -32768 ~ 32767uint16 2 0 ~ 65535int32 4 -2147483648 ~ 2147483647uint32 4 0 ~ 4294967295int64 8 -9223372036854775808 ~ 9223372036854775807uint64 8 0 ~ 18446744073709551615int 平台相關 平台相關uint 平台相關 平台相關uintptr 同指標 在32位平台下為4位元組, 64位平台下為8位元組
對於常規的開發來說,用int和uint就可以了,沒必要用int8之類明確指定長度的類型,以免導致移植困難。
var value2 int32value1 := 64 // value1將會被自動推導為int類型value2 = value1 // 編譯錯誤編譯錯誤類似於:cannot use value1 (type int) as type int32 in assignment。使用強制類型轉換可以解決這個編譯錯誤:value2 = int32(value1) // 編譯通過
eg:
package mainimport "fmt"func main() { var n int16 = 16 var m int32 //m=n m= int32(n) fmt.Printf("32 bit int:%d\n",m) fmt.Printf("16 bit int:%d\n",n)}
2.bool
var v1 boolv1 = truev2 := (1 == 2) // v2也會被推導為bool類型var b boolb = (1!=0) // 編譯正確fmt.Println("Result:", b) // 列印結果為Result: true
3.數值運算
Go語言支援下面的常規整數運算: +、 -、 *、 /和%。 % 和在C語言中一樣是求餘運算,比如:5 % 3 // 結果為: 2
4.浮點型
Go語言定義了兩個類型float32和float64,其中float32等價於C語言的float類型,float64等價於C語言的double類型。
var f1 float32f1 = 12f2 := 12.0 // 如果不加小數點, f2會被推導為整型而不是浮點型f1 = float32(f2)//強制類型轉換
因為浮點數不是一種精確的表達方式,所以像整型那樣直接用==來判斷兩個浮點數是否相等是不可行的,這可能會導致不穩定的結果。
下面是一種推薦的替代方案:
import "math"// p為使用者自訂的比較精度,比如0.00001func IsEqual(f1, f2, p float64) bool { return math.Fdim(f1, f2) < p}
練習:使用math/rand產生10個隨機整數,10個小於100的隨機整數以及10個隨機浮點數
package mainimport ( "fmt" "math/rand" "time")func init() { rand.Seed(time.Now().UnixNano())}func main() { for i := 0; i < 10; i++ { a := rand.Int() fmt.Println(a) } for i := 0; i < 10; i++ { a := rand.Intn(100) fmt.Println(a) } for i := 0; i < 10; i++ { a := rand.Float32() fmt.Println(a) }}
5.字元
在Go語言中支援兩個字元類型,一個是byte(實際上是uint8的別名),代表UTF-8字串,的單個位元組的值;另一個是rune,代表單個Unicode字元。 出於簡化語言的考慮, Go語言的多數API都假設字串為UTF-8編碼。儘管Unicode字元在標準庫中有支援,但實際上較少使用。
byte.go
package mainimport "fmt"func main() { var b byte for b =0;b<177;b++{ fmt.Printf("%d %c\n",b,b) }}
rune.go
package mainimport "fmt"func main() { // byte => uint8 // rune => int32 s := "golang你好" fmt.Println(len(s)) cnt := 0 for _, r := range s { cnt += 1 fmt.Printf("%c\n", r) } fmt.Println("cnt", cnt) ss := []rune("hello") fmt.Println(ss)}
6.字串類型
package mainimport "fmt"func main() { str := "Hello,世界" n := len(str) fmt.Println(n) for i := 0; i < n; i++ { ch := str[i] // 依據下標取字串中的字元,類型為byte fmt.Println(i, ch) } fmt.Println("///////////////////////////") str1 := "Hello,世界" for i, ch := range str1 { fmt.Println(i, ch)//ch的類型為rune }}/*120 721 1012 1083 1084 1115 446 2287 1848 1509 23110 14911 140///////////////////////////0 721 1012 1083 1084 1115 446 199909 30028*/
以位元組數組的方式遍曆 :這個字串長度為12。儘管從直觀上來說,這個字串應該只有10個字元。這是因為每個中文字元在UTF-8中佔3個位元組,而不是1個位元組。
以Unicode字元遍曆: 以Unicode字元方式遍曆時,每個字元的類型是rune(早期的Go語言用int類型表示Unicode字元),而不是byte。
四、實值型別和參考型別
實值型別:基礎資料型別 (Elementary Data Type)int、float、bool、string以及數組和struct。
參考型別:指標、slice、map、chan等都是參考型別。
值語義和引用語義的差別在於賦值,比如下面的例子:
b = a
b.Modify()
如果b的修改不會影響a的值,那麼此類型屬於實值型別。如果會影響a的值,那麼此類型是參考型別。
Go語言中的大多數類型都基於值語義,包括:
基本類型,如byte、 int、 bool、 float32、 float64和string等;
複合類型,如數組(array)、結構體(struct)和指標(pointer)等。
package mainimport "fmt"func main(){ var a = [3]int{1, 2, 3} var b = a b[1]++ fmt.Println(a)// [1 2 3] fmt.Println(b)//[1 3 3]}
b=a指派陳述式是數組內容的完整複製。要想表達引用,需要用指標
package mainimport "fmt"func main(){ var a = [3]int{1, 2, 3} var b = a var c = &a b[1]++ c[1]++ fmt.Println(a) fmt.Println(b) fmt.Println(*c) /* [1 3 3] [1 3 3] [1 3 3] */}
c=&a指派陳述式是數組內容的引用。變數c的類型不是[3]int,而是*[3]int類型
練習:寫一個程式用來列印實值型別和參考型別變數到終端,並觀察輸出結果
package mainimport ( "fmt")func modify(a int) { a = 50 return}func modify1(a *int) { *a = 500}func main() { a := 5 b := make(chan int, 1) fmt.Println("a=", a) fmt.Println("b=", b) modify(a) fmt.Println("a=", a) modify1(&a) fmt.Println("a=", a)}
練習:寫一個程式,交換兩個整數的值。
package mainimport "fmt"func swap(a int, b int) { tmp := a a = b b = tmp return}func main() { one := 1 two := 2 swap(one,two) fmt.Println("one=",one) fmt.Println("two=",two)}
傻眼了,不變!加星號!
package mainimport "fmt"func swap(a *int, b *int) { tmp := *a *a = *b *b = tmp return}func swap2(a int,b int) (int,int){ return b,a}func main() { one := 1 two := 2 swap(&one,&two) //one,two=swap2(one,two) //one,two=two,one fmt.Println(one,two)}
五、變數的範圍
1、在函數內部聲明的變數叫做局部變數,生命週期僅限於函數內部。
2、在函數外部聲明的變數叫做全域變數,生命週期作用於整個包,如果是大寫的,則作用於整個程式。
package mainimport "fmt"var a = "Greg"func aa() { fmt.Println(a)}func ab() { a = "ningxin" fmt.Println(a)}func ac() { a := 2 fmt.Println(a)}func main() { aa() ac() aa() ab() aa()}
a := 2不可能是全域變數,因為這不是聲明,而是執行代碼,代碼塊要在函數裡,go程式從上到下代碼塊外的部分只能是聲明。
var a int a=2
輸出什嗎?
package mainimport "fmt"var a stringfunc f1() { a := "ningxin" fmt.Println(a) f2()}func f2() { fmt.Println(a)}func main() { a = "greg" fmt.Println(a) f1()}