1. 程式人生 > 其它 >go之基本型別與函式使用

go之基本型別與函式使用

go之基本型別與函式使用

目錄

一、go語言介紹

1 go語言介紹

Go 即Golang,是Google公司2009年11月正式對外公開的一門程式語言,特點:語法簡單,速度快。

Go是靜態(編譯型)強型別語言,是區別於解析型語言的弱型別語言(靜態:型別固定 強型別:不同型別不允許直接運算)。

python動態強型別語言。

編譯型語言:Java,c,c++,c#,go

解釋性語言:python,js,php...

編譯型語言涉及到跨平臺問題,因為它需要編譯成該平臺的可執行檔案。比如:java執行在jvm之上。go語言可以進行跨平臺編譯,即交叉編譯。比如,從windows上編譯成linux上的可執行檔案。

解釋性不涉及跨平臺問題,因為各個平臺有各個平臺的直譯器。

2 go語言特性

  • 跨平臺的編譯型語言,交叉編譯
  • 管道(channel),切片(slice),併發(routine)
  • 有垃圾回收的機制
  • 支援面向物件和麵向過程的程式設計模式(go的面向物件中沒有類的概念,python是完全的面向物件)

3 go的歷史發展

  • 2009年11月7日 weekly.2009-11-06 —— 早期的版本
  • 2015年8月19日 go1.5 —— 實現的架構變化,同時保留了和舊版本的相容性,本次更新中移除了”最後殘餘的C程式碼”。(從此以後,自舉,自己寫自己)
  • 2018年8月24日 go 1.11 :增加了modules,包管理
  • 2020 年 8 月 go 1.15

4 go語言的應用領域

Google、Facebook、騰訊、七牛雲

go語言書寫:docker、k8s、藍鯨...

應用領域:服務開發、併發、分散式、微服務等方向。

二、go語言開發環境

1 go環境配置

  1. IDE(Goland): 整合開發環境。
  2. 開發環境:sdk【一路下一步即可】
  3. go語言不需要手動新增到環境變數(python、java需要)
  4. go version : 列印go的版本資訊

注意事項:

  1. goland建立專案:需要選擇go sdk的安裝路徑【預設被選中】。

  2. 指定gopath:程式碼必須放到該路徑下,否則無法執行。【預設會在環境變數中建立GOPATH,需要手動自定義進行修改】。同時,所有的go程式碼必須放在這個路徑下的src資料夾下,否則無法執行【E:\Go_project\src\專案名】。

  3. 路徑和檔案都不要出現中文。

2 go常用命令

go version  // 檢視go的版本資訊
go env  // 檢視go的環境變數
  - GO111MODULE=on  // 使用了go.mod模式
  - GOPATH=E:\Go_project  // 程式碼存放路徑
  - GOROOT=D:\golang  // go sdk安裝路徑
  - ...

go build  // 編譯。編譯型語言,需要先編譯再執行,編譯成可執行檔案
go run  // 編譯並執行。兩步並作一步
go clean  // 清除編譯後的可執行檔案

go fmt  // 格式化程式碼【沒用】
go get  // 下載並安裝包和依賴等同於pip install
...

三、第一個hello world

//單行註釋
/*多行註釋*/

編譯型語言: 
  - 編譯: go build .\test01.go
  - 執行: .\test01.exe
- 編譯並執行: go run .\test01.go
package main  // 宣告包名,包名是main

import "fmt"  // 內建包

// 定義了一個main函式,go專案執行的入口【所以編譯型語言必須有一個入口】
func main() {
	fmt.Println("hello world")  // 等同於python中的print(python中的print是內建函式)
}

注意:每個go檔案都要有一個包名,代表其屬於某個包。

goland中檔案執行有file、package等幾種方式。【可以自己進行配置,一般一個專案中使用package即可,測試的時候使用file就行】

四 變數

1 變數定義

  • var 變數名 變數型別 = 變數值

    var age int = 10

    go中變數定義了就必須使用,如果不適用就報錯。

  • var 變數名 = 變數值 型別推導(可以不寫型別)

    var age = 10

    package main
    
    import "fmt" // 內建包
    
    func main() {
    	var age int = 10
    	var name string = "YangYi"
    	fmt.Printf("%T\n", age)  // int  (看型別,沒有python中的type方法,沒有自動換行,需要手動\n)
    	fmt.Printf("%T\n", name)  // string
    
    	fmt.Print()  // 列印不換行
    	fmt.Println()  // 列印換行
    	fmt.Printf("%T\n", name)  // 檢視型別
    	fmt.Printf("%p", &name)  // 0xc0000881e0
    }
    

    go中變數型別一旦定義,後期不能改變。(編譯型語言通用,python中可以修改,python中存放的記憶體地址,所以可以改變型別)

  • 變數名 := 變數值 簡略宣告(型別和var關鍵字都不寫)

    a := 10

    a := 10
    var a int = 100
    var a = 99  // 重複定義,報錯(變數不可以重複定義,同時,變數必須先定義,後使用)
    
    /* 只定義,不賦值 */
    var a int  // 可以(只能用這種方式)
    var a  // 不可以(因為沒有型別,不知道a是什麼,因為型別需要在定義階段確定,且不能改)
    
/* 宣告多個變數 */
var width, height int = 100, 50  // 第一種
var width, height = 100, 50  // 第二種
width, height := 100, 50  // 第三種

var (
    name   = "YangYi"  // 不賦型別
    age  int  = 29  // 賦型別
    height int  // 宣告
)
/* 小坑 */
var a int =10
var b =99
b,a:=99,100    //這個不報錯,我們覺得是重複定義,冒號左側,只要有一個沒有定義過的變數,就可以

2 變數定義規範

2.1 變數命名規範

變數命令建議用駝峰(大小寫有特殊意義【私有和公有的意義】);
go檔案命名建議用 下劃線;(ps:python中都建議都使用下劃線;java中建議都是駝峰;js中變數名駝峰)

一個名字必須以一個字母(Unicode字母)或下劃線開頭,後面可以跟任意數量的字母、數字或下劃線;

大寫字母和小寫字母是不同的:Name和name是兩個不同的變數;

關鍵字和保留字都不建議用作變數名;

2.2 go語言中的關鍵字

// Go語言中關鍵字有25個
break      default       func     interface   select
case       defer         go       map         struct
chan       else          goto     package     switch
const      fallthrough   if       range       type
continue   for           import   return      var

// Go語言中保留字有37個,主要對應內建的常量、型別和函式
內建常量: true false iota nil
內建型別:  int int8 int16 int32 int64
          uint uint8 uint16 uint32 uint64 uintptr
          float32 float64 complex128 complex64
          bool byte rune string error
內建函式: make len cap new append copy close delete
          complex real imag
          panic recover

五 資料型別

1 基礎資料型別

  • 數字

    // 有符號整形
    int 在32位機器是int32,在64位機器是int64
    int8 表示整數範圍是:8個位元位,8個bit是1byte ,負數和0, 2的7次方-1 的範圍
    int16 2的15次方減一
    int32 
    int64
    /*
    強型別舉例:
    var a int8 = 10
    var b int16 = 20
    fmt.Println(a + b)  // 報錯【go是強型別,其中沒有java中的自動型別轉換一說】
    fmt.Println(int16(a) + b)
    */
    /*
    byte:是int8的別名  單引號包裹
    rune:是int32的別名  單引號包裹
    */
    var a byte = 99  // int8
    fmt.Println(a)
    var b byte = 'b'  // 存放的是字元,其中也是數字而已
    fmt.Println(b)  // 98
    var c rune = '你'  // 可以列印,字元中其實是數字
    fmt.Println(c)  // 20320
    var d byte = '你'  
    fmt.Println(d)  // 報錯,超出範圍
    
    // 無符號整形
    uint8  2的8次方減一 (定義一個人的年齡)
    uint16
    uint32
    uint64
    
    // 浮點數(小數),表示小數點後長度多少位
    float32
    float64
    
    // 複數
    complex64
    complex128
    var a complex64 = 20 + 10i
    
  • 字串

    "雙引號包裹"
    `反引號包裹`  // 和js一樣; python中是 """ """
    
  • 布林型別

    bool true 和 false
    var flag bool = false
    var flag = true
    

資料型別預設值:數字型別是0, 字串型別是空字串,布林型別是false。

java go
byte int8
short int16
int int32
long int64
float float32
double float64

2 常量

go中常量定義使用const關鍵字;java中的常量為final關鍵字;python中是大寫

  • const 變數名 變數型別 = 變數值

    const name string = "yangyi"  // 常量可以定義之後不使用,不會報錯
    
  • const 變數名 = 變數值

    const age = 90
    
  • 其他定義方式

    const name, age = "yangyi", 18
    const (
    	name string = "yangyi"
        age = 18
    )
    
    // 應該不會使用吧
    const (
    	s1 = iota
        s2
        s3 = iota
        s4 = 99
        s5
    )
    fmt.Println(s1)  // 0
    fmt.Println(s2)  // 1
    fmt.Println(s3)  // 2
    fmt.Println(s4)  // 99
    fmt.Println(s5)  // 99 太奇怪
    
    const (
    	s1 = iota
        s2
        s3 = iota
        s4 = 99
        s5 = iota
    )
    fmt.Println(s1)  // 0
    fmt.Println(s2)  // 1
    fmt.Println(s3)  // 2
    fmt.Println(s4)  // 99
    fmt.Println(s5)  // 4 太奇怪
    

六 函式

1 函式基礎

func 函式名(引數名 型別, 引數名 型別)(返回值型別, 返回值型別){
    函式體內容
    return 返回值1, 返回值2
}

函式使用示例

// 1 有引數無返回值
func add(a int, b int){
    fmt.Println(a + b)
}

add(100, 200)  // 呼叫函式  

// 2 有引數無返回值,有多個相同型別的引數
func add(a, b int){
    fmt.Println(a + b)
}

// 3 有引數無返回值,有多個不同型別的引數
func add(a, b int, msg,name string){
}

// 4 多個引數,一個返回值
func add(a, b int) int {
    return a + b
}

// 5 多個引數,多個返回值【python和go支援多返回值】
func add (a, b int) (int, int){
    return a + b, a * b
}

a, b := add(100, 200)  // 多返回值就需要用多變數進行接受【和python一樣】
a, _ := add(3, 4)  // python中的下劃線可以列印,但是go中就是空

// 6 命令返回值
func add(a, b int) (c int, d int){
    c = a + b  // 就是在函式體中已經聲明瞭c d的變數
    d = a * b  
    return  // 就會直接將c d返回出去,不需要寫 return c,d
}

2 函式高階

函式是一等公民,函式可以賦值給變數【python中也是如此】

在編譯型語言中,和解釋性語言不同,沒有先定義函式,後使用函式一說,編譯之後都能找到。

// 匿名函式(定義在函式內部,不能有名字): python中是lamdba
// 第一種
func (){  
}()  // 匿名函式直接呼叫

// 第二種
var a func()  // 定義一個變數,型別為func()【哈哈】
a = func(){  // 此時a是一個函式    
    fmt.Println("我是匿名函式")
}
a()  // 我是匿名函式
// 7 函式返回值為函式
func test() func(){
    return func(){
        fmt.Println("我是返回的函式...")
    }
}
a := test()
fmt.Println(a)  // 0x5451e0 【列印函式名,拿到的還是函式的記憶體地址】


// 8 返回值函式,返回的函式帶引數
// 返回值型別必須跟返回的函式型別匹配 即 func()和func(msg string)不是一個型別
func test() func(msg string){  // 返回值型別必須跟返回的函式型別匹配
    return func(msg string){
        fmt.Println("我是返回的函式...")
    }
}

a := test()
a("hello")  // 函式呼叫

// 9 函式返回值為函式,返回的函式帶引數,帶返回值
func test() func(a, b int) int {
    return func(a, b int) int {
        return a + b
    }
}

a := test()  // a的型別為 var a func(a, b int)int
fmt.Println(a(2, 3))

// 10 函式引數為函式型別,返回值為帶引數帶返回值的函式型別
func test(f func()) func(a, b int) (int, int){
    f()  // f函式執行
    return func(a, b int) (int, int){
        f()  // f函式執行
        return a + b, a * b
    }
}

// 函式操作
a, b := test(
    func(){
        fmt.Println("我是函式引數...")
    }
)(3, 4)  // 函式呼叫

// 或者 
f := func(){
    fmt.Println("我是函式引數...")
}
f1 := test(f)
a, b := f1(3, 4)
fmt.Println(a, b)

3 閉包函式

①定義在函式內部; ②對外部作用域有引用;

閉包函式其實就是多了一種對函式傳參的方式。

func test(age int) func() {
    // age := 18  
    a := func(){  // 定義在函式內部
        fmt.Println(age)  // 對外部作用域有引用
    }
    return a 
}

a := test(19)
a()  // 閉包函式的呼叫【並沒有傳參,實際上已經有引數傳遞進去】

// 一般來說,函式執行完畢,引數應該被釋放,但是由於閉包函式的存在,所以age一直沒有被釋放,有東西對其有引用。【引出記憶體逃逸: 正常應該被回收,其實沒有被回收】

// 裝飾器是閉包函式的典型應用(go語言中沒有裝飾器的語法糖,python中有語法糖)

4 type型別重新命名

type MyFunc func()  // 型別重新命名
func test() MyFunc  {
    return func(){
    }
}

type MyFunc func(a,b int)(int,string)
func test() MyFunc  {
	return func(a,b int)(int,string) {
		fmt.Println("xxx")
		return 10,"ok"
	}
}

type Myint int  // 這裡Myint和int已經不是銅鼓一個型別了
var a int = 10
var b Myint = 100
fmt.Println(a + b)  // 報錯
fmt.Println(a + int(b))  // 強制型別轉換

5 變數的作用域範圍

同一個包下,函式名不能重名。比如:都在main包中

// 第一種方式
var a int   //全域性變數,全域性有效,只要改了,就是改了
func main() {
	fmt.Println(a)  //0
	a=19
	fmt.Println(a) //19
	test1()  //99
	fmt.Println(a) //99
}

func test1()  {
	a=99
	fmt.Println(a)  // 99
}

// 第二種方式【區域性 ——> 全域性 ——> 內建】
var a int   //全域性變數,全域性有效,只要改了,就是改了
func main() {
	var a int
	fmt.Println(a)  //0
	a=19
	fmt.Println(a) //19
	test1()  // 0
	fmt.Println(a) //19
}

func test1()  {
	fmt.Println(a)
}