失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > go语言圣经学习第一天——程序结构总结

go语言圣经学习第一天——程序结构总结

时间:2023-11-22 18:15:07

相关推荐

go语言圣经学习第一天——程序结构总结

文章目录

前言程序结构命名声明变量指针new 函数变量的生命周期赋值类型包和文件

前言

初识Go

Go语言诞生于,是一个年轻却充满活力的语言,该语言从C语言继承了相似的表达式语法、控制流结构、基础数据类型、调用参数传值、指针等思想。最重要的是该语言继承了C语言将源码直接编译为机器码的特色,以及提供了许多对操作系统的接口

如果仅仅是继承了C的特色,却不足以称道,Go在继承了C的同时,又**“模仿”了面向对象语言的特色, 面向对象的基本概念在Go中任然存在,但是却存在一些属于差异。比如说,Go摒弃了面向对象的继承,取而代之的是使用组合的方式,通过在结构体(Structs)之间建立联系**来达到继承的作用。这样有效的避免了继承过深,过多带来的后遗症。

例如以下Go代码使用的就是典型的面向对象的思想:

一个Person对象拥有姓名、地址属性地址又是一个新的对象,拥有街道、城市等属性Person对象拥有 Talk 和 Location 行为我们可以实例化Person对象为p, 然后通过p来调用该对象的行为

type Person struct {Name stringAddress Address}type Address struct {Number stringStreet stringCity stringState stringZip string}func (p *Person) Talk() {fmt.Println("Hi, my name is", p.Name)}func (p *Person) Location() {fmt.Println("I’m at", p.Address.Number, p.Address.Street, p.Address.City, p.Address.State, p.Address.Zip)}func main() {p := Person{Name: "Steve",Address: Address{Number: "13",Street: "Main",City: "Gotham",State: "NY",Zip: "01313",},}p.Talk()p.Location()}

Go是面向对象语言吗

看了上边的代码,大家觉得Go是面向对象语言吗?官方给的答案是yes and no,至于是否真的是面向对象语言个人觉得并不是那么重要了,难道我们吃个完整的苹果和切成拼盘的苹果,会觉得口味差异很大吗?

集众家之所长

类似Python拥有自己的标准库和第三方库社区,可以直接通过命令下载第三方包。拥有类似java和Python的垃圾回收机制拥有类似Python的匿名函数,切片特性,以及一些其他语言的好的特性等类似于Python的解包可以同时给多个变量赋值

程序结构

命名

Go的命名规则和其他语言一样,不能和标识符与关键字等官方命名冲突。

名字必须以字母或下划线开头,大写字母表示为公开的,相当于java的public, 是可以被外部包引用的。

如果不想被外部引用可以小写字母开头,相当有java的private

Go推荐使用驼峰命名法。

声明

分别使用var、const、type和func 声明变量、常量、类型和函数实体对象。

在一个go文件中声明顺序如下:

// Boiling prints the boiling point of water.// 必须先声明该源文件属于哪个包package main// 然后声明导入的内置包或者第三方包import "fmt"// 声明包一级的常量const boilingF = 212.0// 声明函数func main() {// 声明函数内局部变量var f = boilingFvar c = (f - 32) * 5 / 9fmt.Printf("boiling point = %g°F or %g°C\n", f, c)// Output:// boiling point = 212°F or 100°C}

变量

变量的声明必须指定类型或者指定初始化值:

// 当指定类型没有指定初始化值时,默认为对应类型的空值var s stringfmt.Println(s) // ""

可以同时声明多个变量:

var i, j, k int // int, int, intvar b, f, s = true, 2.3, "four" // bool, float64, string

也可以接收表达式或者函数的返回值赋值给声明变量:

var f, err = os.Open(name) // os.Open returns a file and an error

简短变量声明名字 := 表达式

anim := gif.GIF{LoopCount: nframes}freq := rand.Float64() * 3.0t := 0.0// 简短变量声明也可以同时声明多个变量i, j := 0, 1

交换变量的值,学过Python的应该很熟悉:

i, j = j, i // 交换 i 和 j 的值

当使用简短变量声明变量的时候,如果在相同命名空间中重复声明,第二次相当于对变量进行赋值:

// 声明in err 变量in, err := os.Open(infile)// 声明out变量 并对err变量赋值out, err := os.Create(outfile)

指针

和C语言中的指针一样,一个指针对应指向变量在内存中的位,同时指针也是一个变量:

&x:表示取址,取变量x的内存地址

*p:表示取值,对指针p 指向的变量的值进行操作,可以获取指针p指向的变量的值,也可以 重新给指向变量赋值。

// 定义一个变量x x := 1// 定义一个指针变量 指向变量x的地址p := &x // p, of type *int, points to xfmt.Println(*p) // "1"// *p = 2// equivalent to x = 2fmt.Println(x) // "2"

任何类型的指针的零值都是nil

如果p指向某个有效变量,那么p != nil 测试为真。

指针间也可以进行相等测试,只有当指向同一个变量或全部是nil时才相等。

var x, y intfmt.Println(&x == &x, &x == &y, &x == nil) // "true false false"

函数返回结果也可以返回函数中局部变量的地址:

var p = f()func f() *int {v := 1return &v}

指针也可以作为参数传递,并且可以在函数中修改指针指向变量的值,返回指向变量新的值:

func incr(p *int) int {*p++ // 非常重要:只是增加p指向的变量的值,并不改变p指针!!!return *p}v := 1incr(&v) // side effect: v is now 2fmt.Println(incr(&v)) // "3" (and v is 3)

new 函数

也可以使用new(T)创建T类型的匿名变量:

p := new(int) // p, *int 类型, 指向匿名的 int 变量fmt.Println(*p) // "0"*p = 2// 设置 int 匿名变量的值为 2fmt.Println(*p) // "2"

每次调用new 函数都是返回一个新的变量地址:

p := new(int)q := new(int)fmt.Println(p == q) // "false"

变量的生命周期

Go语言有自己的垃圾回收机制,该回收机制和Python的差不多,都是根据变量是否还在被引用中。

虽然Go有自己的垃圾回收机制,但是要想编写高效的程序,一定要注意定义变量的声明周期:

例如在函数内部定义了一个局部变量,只在函数内部使用,但是却使用了global 将其保存到了全局变量中,这样就会导致内存的浪费,影响程序的性能。

赋值

x = 1 // 命名变量的赋值*p = true // 通过指针间接赋值person.name = "bob" // 结构体字段赋值 类似count[x] = count[x] * scale // 数组、slice或map的元素赋值count[x] *= scale

元组赋值

就是同时给多个变量赋值,和Python的解包给多个变量赋值一样的:

f, err = os.Open("foo.txt") // function call returns two values

可赋值性

Go是不允许变量进行隐式类型转换的,所以在赋值的类型一定要与定义的变量类型一致:

medals := []string{"gold", "silver", "bronze"}medals[0] = "gold"medals[1] = "silver"medals[2] = "bronze"

类型

定义变量需要显性或者隐性的指明变量类型,但一个变量只能表示一个事物,有时候我们需要定义一个能够表示相同类型的一类事物的时候,就出现了自定义类型的概念。

package tempconvtype Celsius float64 // 摄氏温度type Fahrenheit float64 // 华氏温度const (AbsoluteZeroC Celsius = -273.15 // 绝对零度FreezingCCelsius = 0 // 结冰点温度BoilingCCelsius = 100// 沸水温度)// 下边函数返回使用Fahrenheit() 和 Celsius() 是类型转换,如int(32.6)func CToF(c Celsius) Fahrenheit {return Fahrenheit(c*9/5 + 32) }func FToC(f Fahrenheit) Celsius {return Celsius((f - 32) * 5 / 9) }

如上定义了摄氏温度类型和华氏温度类型,这两种类型的底层都是float64类型。

然后在定义常量或者变量的时候就可以使用类型定义,函数传参的时候使用自定义类型表明参数类型,和使用内置类型是一样的。

上边的摄氏温度类型和华氏温度类型虽然底层相同,但是是不同的类型,Go中不同的类型是不能相互比较和赋值

var c Celsiusvar f Fahrenheitfmt.Println(c == 0)// "true"fmt.Println(f >= 0)// "true"fmt.Println(c == f)// compile error: type mismatchfmt.Println(c == Celsius(f)) // "true"!

自定义类型支持底层类型的一些操作,如摄氏温度类型和华氏温度类型底层是float,该自定义类型就支持float运算。

自定义类型的好处

在Go中除了内置类型,我们还可以定义结构体类型,上边自定义类型底层使用float 可能并没有真正使程序简单很多。

但是如果自定义类型是结构体类型,我们就可以给该类型的值定义新的行为,这些行为表示为关联到该类型的函数集合,称为类型的方法集

包和文件

在同一个文件夹目录下的源文件都属于同一个包。同一个包中定义的全局声明在包中的所有源文件都可以访问。在每个源文件的包声明前紧跟着的注释是包注释。一个包通常只有一个源文件有包注释,如果有多个包注释,文档工具会根据文件名的先后顺序将它们链接为一个包注释。如果包注释很大,通常会放到一个独立的doc.go文件中。

导入包

在包中导入别的包后,我们可以包名.声明去访问别的包的内容。

如果同时导入的两个包可能名字相同,但是内容不同,为了避免导入冲突,可以给导入的包起别名:

package mainimport io "fmt" //引用fmt这个包时,名字重命名为iofunc main() {io.Println("aaaaaaaaaaa")}

包的初始化

包在导入的时候,会且仅会被初始化一次,初始化工作是自下而上进行的,main包最后被初始化。包中的每一个源文件都可以有自己的初始化函数,一个文件中的初始化函数可以有一个或多个,单个文件中如果有多个初始化函数,会按照声明的顺序初始化。

如果觉得《go语言圣经学习第一天——程序结构总结》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。