Golang 知识点

变量声明

var a
a=100
//或
var b = 100
//或
var c int = 100

// := 是声明并赋值,并且系统自动推断类型,不需要var关键字
d := 100

package main

import "fmt"

func main() {
    // 切片的一种定义方式是 用 make
    // var x = make([]float64, 5) ,切片用make
    //另外一种是通过数组切片赋值,采用[low_index:high_index]的方式获取数值切片,其中切片元素包括low_index的元素,但是不包括high_index的元素。

    ar := new([3]int)// 指针
    ar1 := [3]int{0, 0, 0}
    var ar2 = [3]int{0, 0, 0}
    var ar3 [3]int
    var ar4 = [3]int{0: 1, 1: 2, 2: 3}
    fp(ar)
    fp(&ar1)
    fp(&ar2)
    fp(&ar3)
    fp(&ar4)

    fp1(ar)
    fp1(&ar1)
    fp1(&ar2)
    fp1(&ar3)
    fp1(&ar4)

}

func fp(a *[3]int)  { print(*a) }
func fp1(a *[3]int) { print(a) }

与 Python不同

任何空值(nil)或者零值(0, 0.0, "")都不能作为布尔型来直接判断。

func main() {
    if 0 {
        fmt.Println("hello world")
    }
    if nil {
        fmt.Println("hello world")
    }
    if "" {
        fmt.Println("hello world")
    }
}

常用命令

go build: 编译出可执行文件
go install: go build + 把编译后的可执行文件放到GOPATH/bin目录下
go get: git clone + go install

何时使用指针

指针的一大用途就是可以将变量的指针作为实参传递给函数,从而在函数内部能够直接修改实参所指向的变量值。

Go的变量传递都是值传递。

package main

import (
    "fmt"
)

func change(x int) {
    x = 200
}
func main() {
    var x int = 100
    fmt.Println(x)
    change(x)
    fmt.Println(x)
}
上面的例子输出结果为

100
100
很显然,change函数改变的仅仅是内部变量x的值,而不会改变传递进去的实参。其实,也就是说Go的函数一般关心的是输出结果,而输入参数就相当于信使跑到函数门口大叫,你们这个参数是什么值,那个是什么值,然后就跑了。你函数根本就不能修改它的值。不过如果是传递的实参是指针变量,那么函数一看,小子这次你地址我都知道了,哪里跑。那么就是下面的例子:

package main

import (
    "fmt"
)

func change(x *int) {
    *x = 200
}
func main() {
    var x int = 100
    fmt.Println(x)
    change(&x)
    fmt.Println(x)
}
上面的例子中,change函数的虚参为整型指针变量,所以在main中调用的时候传递的是x的地址。然后在change里面使用*x=200修改了这个x的地址的值。所以x的值就变了。这个输出是:

100
200

一个函数何时该用指针类型做receiver对初学者而言一直是个头疼的问题。如果不知道该如何取舍,选择指针类型的receiver。但有些时候value receiver更加合适,比如对象是一些轻量级的不变的structs,使用value receiver会更加高效。下面是列举了一些常用的判断指导。

如果receiver是map、func或者chan,不要使用指针

如果receiver是slice并且该函数并不会修改此slice,不要使用指针

如果该函数会修改receiver,此时一定要用指针

如果receiver是struct并且包含互斥类型sync.Mutex,或者是类似的同步变量,receiver必须是指针,这样可以避免对象拷贝

如果receiver是较大的struct或者array,使用指针则更加高效。多大才算大?假设struct内所有成员都要作为函数变量传进去,如果觉得这时数据太多,就是struct太大

如果receiver是struct,array或者slice,并且其中某个element指向了某个可变量,则这个时候receiver选指针会使代码的意图更加明显

如果receiver使较小的struct或者array,并且其变量都是些不变量、常量,例如time.Time,value receiver更加适合,因为value receiver可以减少需要回收的垃圾量。

最后,如果不确定用哪个,使用指针类的receiver

常量

变量定义的类型推断方式 := 不能够用来定义常量

快速声明

Go还提供了一种同时定义多个变量或者常量的快捷方式。


import (
"fmt"
)

func main() {
    var (
        a int     = 10
        b float64 = 32.45
        c bool    = true
    )
    const (
        Pi   float64 = 3.14
        True bool    = true
    )

    fmt.Println(a, b, c)
    fmt.Println(Pi, True)
}

if 判断的()

package main

import (
"fmt"
)

func main() {
const Male = 'M'
const Female = 'F'

var dog_age = 10
var dog_sex = 'M'

if (dog_age == 10 && dog_sex == 'M') {
    fmt.Println("dog")
}

}


但是如果你使用Go提供的格式化工具来格式化这段代码的话,Go会智能判断你的括号是否必须有,否则的话,会帮你去掉的。你可以试试。

go fmt test_bracket.go
然后你会发现,咦?!果真被去掉了。

#  for 循环

package main

import (
"fmt"
)

在Go里面没有提供while关键字,如果你怀念while的写法也可以这样:

func main() {
    var i int = 1

    for i <= 100 {
        fmt.Println(i)
        i++
    }
}
或许你会问,如果我要死循环呢?是不是for true?呵呵,不用了,直接这样。

for{

}

切片

我们发现arr1的长度变为11,因为元素个数现在为11个。另外我们发现arr1的容量也变了,变为原来的两倍。

这是因为Go在默认的情况下,如果追加的元素超过了容量大小,Go会自动地重新为切片分配容量,容量大小为原来的两倍。

总结一下,数组和切片的区别就在于[]里面是否有数字或者...!!!!!!!!!!!

因为数值长度是固定的,而切片是可变的。

函数预定义

命名返回值

Go的函数很有趣,你甚至可以为返回值预先定义一个名称,在函数结束的时候,直接一个return就可以返回所有的预定义返回值。例如上面的例子,我们将sum作为命名返回值。

package main

import (
"fmt"
)

func slice_sum(arr []int) (sum int) {
    sum = 0
    for _, elem := range arr {
        sum += elem
    }
    return
}

func main() {
    var arr1 = []int{1, 3, 2, 3, 2}
    var arr2 = []int{3, 2, 3, 1, 6, 4, 8, 9}
    fmt.Println(slice_sum(arr1))
    fmt.Println(slice_sum(arr2))
}

这里要注意的是,如果你定义了命名返回值,那么在函数内部你将不能再重复定义一个同样名称的变量。比如第一个例子中我们用sum := 0来定义和初始化变量sum,而在第二个例子中,我们只能用sum = 0初始化这个变量了。因为 := 表示的是定义并且初始化变量。

异常处理

panic & recover

当你周末走在林荫道上,听着小歌,哼着小曲,很是惬意。突然之间,从天而降瓢泼大雨,你顿时慌张(panic)起来,
没有带伞啊,淋着雨感冒就不好了。于是你四下张望,忽然发现自己离地铁站很近,那里有很多卖伞的,心中顿时又安定了下来(recover),于是你飞奔过去买了一把伞(defer )

Go语言提供了关键字defer来在函数运行结束的时候运行一段代码或调用一个清理函数。上面的例子中,虽然second()函数写在first()函数前面,但是由于使用了defer标注,所以它是在main函数执行结束的时候才调用的。


package main

import (
    "fmt"
)

func main() {

    //defer一定是在函数执行结束的时候运行的。不管是正常结束还是异常终止,相当于finally
    defer func() {
        //panic用来触发异常,而recover用来终止异常并且返回传递给panic的值。(注意recover并不能处理异常,而且recover只能在defer里面使用,否则无效。)
        msg := recover()
        fmt.Println(msg)
    }()
    fmt.Println("I am walking and singing...")
    panic("It starts to rain cats and dogs")
}

指针

定义
Go函数的参数传递方式是值传递
而指针的主要作用就是在函数内部改变传递进来变量的值

至于使不使用结构体指针和使不使用指针的出发点是一样的,那就是你是否试图在函数内部改变传递进来的参数的值。再举个例子如下:

在学Python的时候,经常要注意不小心修改了可变类型,如list。事实上这种可变和不可变了类型,是Python为了简化你的操作。

但是在Golang中,数组和切片,你可以当做可变类型,也可以当做不可变类型,来使用,通过使用*,&指针。

所谓指针其实你可以把它想像成一个箭头,这个箭头指向(存储)一个变量的地址。

因为这个箭头本身也需要变量来存储,所以也叫做指针变量。

new 的使用发现


package main

import (
    "fmt"
)

func set_value(x_ptr *int) {

//指针指向的地址内容设置为100
    *x_ptr = 100
}
func main() {
    //开辟一块内存,用于存贮指针地址,且此指针地址的变量名为x_ptr
    x_ptr := new(int)

    set_value(x_ptr)

    //指针变量指向的地址,因为本身就是指针变量,存贮的就是指向的地址
    fmt.Println(x_ptr)
    //指针变量本身的地址
    fmt.Println(&x_ptr)

    //打印指针变量指向的地址内容
    fmt.Println(*x_ptr)
}

0xc084000040
0xc084000038
100

结构体组合函数

上面我们在main函数中计算了矩形的面积,但是我们觉得矩形的面积如果能够作为矩形结构体的“内部函数”提供会更好。这样我们就可以直接说这个矩形面积是多少,而不用另外去取宽度和长度去计算。现在我们看看结构体“内部函数”定义方法:

package main

import (
"fmt"
)

type Rect struct {
width, length float64
}

func (rect Rect) area() float64 {
return rect.width * rect.length
}

func main() {
var rect = Rect{100, 200}

fmt.Println("Width:", rect.width, "Length:", rect.length,
    "Area:", rect.area())

}
咦?这个是什么“内部方法”,根本没有定义在Rect数据类型的内部啊?

确实如此,我们看到,虽然main函数中的rect变量可以直接调用函数area()来获取矩形面积,但是area()函数确实没有定义在Rect结构体内部,这点和C语言的有很大不同。Go使用组合函数的方式来为结构体定义结构体方法。我们仔细看一下上面的area()函数定义。

首先是关键字func表示这是一个函数,第二个参数是结构体类型和实例变量,第三个是函数名称,第四个是函数返回值。这里我们可以看出area()函数和普通函数定义的区别就在于area()函数多了一个结构体类型限定。这样一来Go就知道了这是一个为结构体定义的方法。

这里需要注意一点就是定义在结构体上面的函数(function)一般叫做方法(method)

接口和鸭子类型

package main

import (
    "fmt"
)

type Phone interface {
    call()
}

type NokiaPhone struct {
}

func (nokiaPhone NokiaPhone) call() {
    fmt.Println("I am Nokia, I can call you!")
}

type IPhone struct {
}

func (iPhone IPhone) call() {
    fmt.Println("I am iPhone, I can call you!")
}

func main() {
    var phone Phone

    phone = new(NokiaPhone)
    phone.call()

    phone = new(IPhone)
    phone.call()

}

以前我们说过,Go语言式静态类型语言,变量的类型在运行过程中不能改变。但是在上面的例子中,phone变量好像先定义为Phone类型,然后是NokiaPhone类型,最后成为了IPhone类型,真的是这样吗?

在Go语言里面,一个类型A只要实现了接口X所定义的全部方法,那么A类型的变量也是X类型的变量。
就是,你能叫,能游,你就是鸭子

数组和指针

package main
import "fmt"

func main() {
array := [3]float64{7.0, 8.5, 9.1}
x := Sum(&array) // Note the explicit address-of operator
// to pass a pointer to the array
fmt.Printf("The sum of the array is: %f", x)
}

func Sum(a *[3]float64) (sum float64) {
for _, v := range a { // derefencing *a to get back to the array is not necessary!
sum += v
}
return
}

*表示,你不用来,我去干你,我只接受你的地址,不接受你。

&表示我告诉你的我的地址

不用*指针的方式的话,会复制一份数组,浪费内存

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容