一、文件名&關鍵字&標識符
- 所有go源碼都是以.go結尾
- 標識符以字母或下划線開頭,大小寫敏感
- 下划線_是特殊標識符,用戶忽略結果
- 保留關鍵字
- 導入包時可以設置別名
下面是保留關鍵字:
二、GO程序的基本結構
package main import ( "fmt" ) func main(){ fmt.Println("Hello world") }
- 任何一個代碼必須隸屬一個包
- import關鍵字,引入其他包
- golang可執行程序,package main 並且只有一個main入口函數
- 包中函數的調用,同一個函數中可以直接調用;不同包中函數,通過包名+點+函數名進行調用
- 包訪問控制規則,大寫意味着這個函數或者變量可以導出的,及在其他包內是可以調用的;小寫以為這個函數或者變量是私有的,包外部不能訪問。
小練習1
寫一個小程序,對於給定的一個數字n,求出所有兩兩相加等於n的組合
package main import ( "fmt" ) func add_num(n int){ for i:=0;i<=n;i++{ fmt.Printf("%d+%d=%d\n",i,n-i,n) } } func main(){ add_num(5) }
小練習2
寫一個小程序,包含兩個包,add和main,其中add包中有兩個變量:Name,和age,請問main包中如何訪問Name和age?(用於理解go中大小寫敏感的問題)
main包中的代碼:
package main import ( "fmt" "go_dev/day02/kexia02/add" ) func main() { fmt.Println(add.Name) }
add包中的代碼
package add var Name string = "zhaofan" var age int = 23
從結果我們可以發現我們在main包中是不能調用到add包中的age,但是是可以調用到Name
這里就是因為大小寫的問題,go中的大寫可以理解為其他語言中的public,小寫理解為private
這里有個問題需要注意:
我們把add包中的代碼改為:
package add var Name string var Age int Name = "zhaofan" Age = 23
這樣也是錯誤的寫法,go作為編譯型語言,必須通過函數來進行語句的執行,而不能在函數外執行語句
小練習3
開發一個小程序,使用包別名來訪問包中的函數或變量
直接對上一個程序的main包進行更改
package main import ( "fmt" a "go_dev/day02/kexia02/add" ) func main() { fmt.Println(a.Name) fmt.Println(a.Age) }
小練習4
每個源文件都可以包含一個init函數,這個init函數自動被go運行框架調用,通過下面例子演示:
package main import ( "fmt" ) func init(){ fmt.Println("執行初始化函數") } func main() { fmt.Println("hello world") }
運行結果是先打印了init函數的內容,后打印了main函數中的問題,所以init函數先與main函數執行
三、函數的聲明和注釋
函數聲明
格式為:func 函數名字(參數列表)(返回值列表)
例子如下:
func add(){
}
func add(a int,b int) int{
}
func add(a int,b int) (int int){
}
注釋
單行注釋//
多行注釋/* */
四、GO語言的數據類型和操作符
常見數據類型及分類
GO語言按照類別分為幾下幾種數據類型:
布爾型:true或false,例子:var b bool = true
數字類型:包括整型int和浮點float
字符串類型:這里強調一下,GO的字符串是由單個字節連接起來的Go語言的字符串的字節使用UTF-8編碼標識Unicode文本。
派生類型:這里包括指針類型,數組類型,結構化類型,Channel類型,函數類型,接口類型,Map類型
注意:
字符串的時候用雙引號”“,這里也可以用反引號`,通過反引號的方式會保留你的格式,不會對你的內容做任何轉義
字節的時候用單引號‘’,同樣也可以通過反引號`
var cc byte = 'c' fmt.println(ccc)
var cc byte = c
fmt.println(cc)
一個會打印c的ASCII,一個會打印c
關於fmt.Printf()的用法
官網地址:https://go-zh.org/pkg/fmt/
一般
%v 相應值的默認格式。在打印結構體時,“加號”標記(%+v)會添加字段名
%#v 相應值的Go語法表示
%T 相應值的類型的Go語法表示
%% 字面上的百分號,並非值的占位符
布爾
%t 單詞 true 或 false。
整數
%b 二進制表示
%c 相應Unicode碼點所表示的字符
%d 十進制表示
%o 八進制表示
%q 單引號圍繞的字符字面值,由Go語法安全地轉義
%x 十六進制表示,字母形式為小寫 a-f
%X 十六進制表示,字母形式為大寫 A-F
%U Unicode格式:U+1234,等同於 "U+%04X"
浮點數及其復合構成
%b 無小數部分的,指數為二的冪的科學計數法,與 strconv.FormatFloat
的 'b' 轉換格式一致。例如 -123456p-78
%e 科學計數法,例如 -1234.456e+78
%E 科學計數法,例如 -1234.456E+78
%f 有小數點而無指數,例如 123.456
%g 根據情況選擇 %e 或 %f 以產生更緊湊的(無末尾的0)輸出
%G 根據情況選擇 %E 或 %f 以產生更緊湊的(無末尾的0)輸出
字符串與字節切片
%s 字符串或切片的無解譯字節
%q 雙引號圍繞的字符串,由Go語法安全地轉義
%x 十六進制,小寫字母,每字節兩個字符
%X 十六進制,大寫字母,每字節兩個字符
指針
%p 十六進制表示,前綴 0x
通過fmt.Printf() 可以格式化輸出到終端,如果想要格式化存儲到變量則是fmt.Sprintf()
數字類型
數字類型包括了:
uint8(無符號8位整型,0到255)
uint16(無符號16位整型,0到65535)
uint32(無符號32位整型,0到4294967295)
unint64(無符號64位整型,0到18446744073709551615)
int8(有符號8位整型,-128到127)
int16(有符號16位整型,-32768到32767)
int32(有符號32位整型 ,-2147483648 到 2147483647)
int64(有符號64位整型 ,-9223372036854775808到9223372036854775807)
浮點型
flat32: 32位浮點型數
flag64: 64位浮點型數
complex64:32 位實數和虛數
complex128:64 位實數和虛數
類型轉換
舉個例子來理解: var a int = 8 轉換為int32 var b int32 = int32(a)
當我們代碼中設計到數據計算的時候,要保證兩個數據類型完全相同,
var a int 和 var b int32 是不能直接做計算處理的,這個時候就需要用到類型轉換
相關操作符
! && || 分別表示非,與,或
== 、=、!=、 <、>、<=、>=
練習1
使用math/rand生成隨機整數,10個小於100的隨機整數以及10個隨機浮點數
package main import ( "fmt" "math/rand" ) func rand_print(){ for i:=0;i<10;i++{ fmt.Println(rand.Int31n(100)) fmt.Println(rand.Float32()) } } func main() { rand_print() }
但是這里會造成每次運行程序生成的隨機數是相同的,解決方式是加入隨機種子:rand.Seed(time.Now().Unix())
五、常量
常用cost修飾,代表永遠是只讀不能修改
const只能修飾boolean,number(int相關類型,浮點類型,complex)和string
語法
const 變量名 [變量類型] = value其中變量類型可以省略
例子
const b string = "hello"
const b int = 23
通常定義常亮的寫法
const (
a = 0
b = 1
)
高級方法
const (
a = iota
b
c
)
這里自動回吧a賦值為0,后面的變量一次加1
六、變量
聲明一個變量的形式是使用var關鍵字格式如下:
var 變量名 變量類型
關於變量的聲明常用的有三種方法:
- 指定變量類型,聲明變量后不賦值,使用默認值。如果是數據類型是int,默認值為0,如果數據類型是字符串string,默認值為空,如果是布爾類型默認為false
- 不指定變量類型,根據值自行判斷變量類型(個人不建議這種方法,go是強類型語言,定義變量最好指定變量類型)
- 省略var關鍵字通過:=方式定義變量,並賦值。例子name := "zhaofan"就相當於var name string然后name = "zhaofan"
變量的作用域
在函數內部聲明的變量叫做局部變量,生命周期僅限於函數內部
在函數外部生命的變量叫做全局變量,生命周期作用域整個包,如果是大寫,作用域整個程序
在函數內的語句塊的內的變量只在語句塊,生命周期就是僅限於語句塊內.這里主要指{} 括起來的語句塊
通過下面的例子理解
例子1
package main import ( "fmt" ) var a string = "zhaofan" func n(){ fmt.Println(a) } func m(){ a := "Jack" fmt.Println(a) } func main(){ n() m() n() }
結果會依次打印zhaofan,Jack,zhaofan
這里就是驗證了函數內部的定義的變量,生命周期僅限於函數內部
如果我們把m函數中的a:="Jack"改為a = "Jack"
結果為:zhaofan,Jack,Jack
例子2
package main import ( "fmt" ) var a string func f1(){ a := "Jack" fmt.Println(a) f2() } func f2(){ fmt.Println(a) } func main(){ a = "zhaofan" fmt.Println(a) f1() }
這個程序的結果會打印zhaofan,Jack,zhaofan
其實這里還是要理解上面的說的關於變量的作用域
值類型和引用類型
值類型:變量直接存儲值,即變量直接指向存在內存中的值。內存通常在棧中分配
值類型通常包括基本數據類型int,float,bool,string以及數組和struct
var i int = 5
var j int = i
這個時候i和j的值都為5
其實這里內存中是將i的值進行了拷貝,我們通過&獲取變量的內存地址也可以看出
&i和&j 的內存地址是不同的,所以我如果在var j int = i,后繼續更改i變量的值,並不會影響j的值,因為是不同的內存地址存放的值
引用類型:變量存的是一個地址,這個地址存儲最終的值,內存通常在堆上分配。通過GC回收
一個引用類型的變量 r1 存儲的是 r1 的值所在的內存地址(數字),或內存地址中第一個字所在的位置。
引用類型通常包括指針,slice,map,chan等。
所以如果我們定義了兩個引用類型變量像上面的值類型一樣進行賦值操作則兩者都會改變,因為兩者的引用地址是相同的。
所以在引用類型中變量是拷貝的內存地址,而地址最終指向值
小練習1
package main import ( "fmt" ) func main() { var a int = 100 var b chan int = make(chan int,1) fmt.Println("a=",a) fmt.Println("b=",b) }
結果為:
從這里我們也可以看出值類型和引用類型的區別
小練習2
寫一個小程序,交換兩個整數的值
package main import ( "fmt" ) func swap(a int,b int){ a,b = b,a fmt.Println("a=",a,"b=",b) } func main() { change(5,4) }