Go 语言入门指南:基础语法和常用特性解析 | 青训营

47 阅读7分钟

Go语言背景

Go 语言是谷歌 2009 年首次推出并在 2012 年正式发布的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。

谷歌首席软件工程师表示之所以开发 Go,是因为过去10多年间软件开发的难度令人沮丧。Google 对 Go 寄予厚望,其设计是让软件充分发挥多核心处理器同步多工的优点,并可解决面向对象程序设计的麻烦。

它具有现代的程序语言特色,如垃圾回收,帮助开发者处理琐碎但重要的内存管理问题。

Go 的速度也非常快,几乎和 C 或 C++ 程序一样快,且能够快速开发应用程序。

Go语言基本特性

  1. 高性能、高并发,语法层支持并发,和拥有同步并发的channel类型,使并发开发变得非常方便。
  2. 语法简单、学习曲线平缓
  3. 丰富的标准库
  4. 完善的工具链
  5. 静态链接
  6. 快速编译
  7. 跨平台
  8. 垃圾回收
  9. 简单的思想,没有继承,多态,类等。

Go语言基础语法

常量

常量在编译时就已经确定

const NAME string = "123" //显示
const VALUE = 1 //隐示

变量

变量声明的两种方式:

  1. 使用关键字var进行变量声明
  2. 短声明,编译器自动推断变量的类型
var num1 int = 2  
num2 := 3 //短声明

数据类型

Go语言中的基本数据类型有整型,布尔,浮点数,字符串,字节类型等。

整型

整型分为以下两个大类: 按长度分为:

有符号整型int8int16int32int64

无符号整型:uint8uint16uint32uint64

其中,uint8就是我们熟知的byte型,int16对应C语言中的short型,int64对应C语言中的long型。

字符串类型:

Go语言中的字符串以原生数据类型出现,使用字符串就像使用其他原生数据类型一样。 Go 语言里的字符串的内部实现使用UTF-8编码。 字符串的值为双引号(")中的内容,

字符串的内部结构如下

type _string struct {
    elements *byte // 引用着底层的字节
    len      int   // 字符串中的字节数
}

Go语言中的字符串和Java中的字符串特性相似,其字符串值的内容(即底层字节)是不可更改的。字符串值的长度也是不可独立被更改的。一个可寻址的字符串只能通过将另一个字符串赋值给它来整体修改它。(gfw.go101.org/article/str…)

以下是一个对字符串进行修改的例子:

str1 := "hello world"  //"hello world" 
bytestr1 := []byte(str1)  
bytestr1[0] = 'k'  
str2 := string(bytestr1) //"kello world"

我们可以使用aString[start:end]来获取aString的一个子字符串。 这里,startend均为aString中存储的字节的下标。

对于标准编译器来说,一个字符串的赋值完成之后,此赋值中的目标值和源值将共享底层字节。 一个子切片表达式aString[start:end]的估值结果也将和基础字符串aString共享一部分底层字节。

以下是一个获取子字符串的例子

str := "hello world"  
substr1 := str[0:3]  //"hel"
substr2 := str[1:] //ello world

数组

Go中有三种一等公民容器类型:数组、切片和映射。

数组是 元素类型相同 且长度固定 的存储在内存中数据类型。

数组存储的类型可以是内置类型,比如整型或者字符串,也可以是自定义的数据结构。

当声明了一个数组array,这个时候数组array里面的值,是对应元素类型的零值

数组一旦声明后,其元素类型和大小都不能改变了

var arr [5]int  //初始化为5个0

要注意的是,与其他语言不同,在函数间传递数组时,传递的是值的拷贝,也就是值传递,而不像C语言和Java语言中是传递的地址。因此如果需要通过函数改变数组的值,是需要传递数组的引用

如下是一个例子:

func modify(a *[5]int) {
    (*a)[1] = 3
    a[3] = 4 
}

func main() {
    array := [5]int{0, 1, 2, 3, 4}
    modify(&array)
    fmt.Println(array) //输出结果[0 3 2 4 4]
}

slice切片

Go中有三种一等公民容器类型:数组、切片和映射。

它和数组非常相似,是围绕 动态数组 的概念设计的,可以按需自动改变大小。

切片的内部结构如下

type _slice struct {
	elements unsafe.Pointer // 引用着底层存储在间接部分上的元素
	len      int            // 长度
	cap      int            // 容量
}

切片值的内存布局:

image.png

根据上面的内存布局可以得知,通过以下这种方式修改切片值的话,新的切片和旧的切片值是共享底层内存空间的,因此会同时改变两个切片的值。

slice := []int{1, 2, 3, 4, 5}  
newSlice := slice[1:3]  
newSlice[0] = 10  
fmt.Println(slice)  //[1 10 3 4 5]
fmt.Println(newSlice) //[10 3]

注意,与数组不同,在函数间传递切片,这个时候传递的就是引用类型了。

切片是3个字段构成的结构类型,所以在函数间以值的方式传递的时候,占用的内存非常小,成本很低。在传递复制切片的时候,其底层数组不会被复制,也不会受影响,复制只是复制的切片本身,不涉及底层数组。

func modifySlice(slice []int) {
    slice[1] = 10
}
func main() {
    slice := []int{1, 2, 3, 4, 5}
    modifySlice(slice)
    fmt.Println(slice) //[1 10 3 4 5]
}

映射map

Go中有三种一等公民容器类型:数组、切片和映射

Map是一种数据结构,是一个集合,用于存储一系列无序的键值对。它基于键存储的,键就像一个索引一样,这也是Map强大的地方,可以快速快速检索数据,键指向与该键关联的值。

在函数间传递map是传递的引用类型

结构体

Go语言中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型就无法满足需求了,我们可以通过struct来定义自己的类型。

和C语言类似,Go语言中也有结构体。由type 结构体名称 struct开头,后面跟着用一对大括号{},其中包裹着的一系列字段(field)声明。 一般来说,每个字段声明由一个字段名和字段类型组成。一个结构体类型的字段数目可以为0。

type Book struct {
    title, author string
    pages         int
}

Go语言中是没有面向对象(如继承和构造函数)相关的特性,但是可以通过struct来实现

注意在函数间传递struct是传递的值类型

指针

一个指针可以存储一个内存地址

Go指针在使用上相对于C指针有很多限制。 通过施加这些限制,Go指针保留了C指针的好处,同时也避免了C指针的危险性。

  1. Go指针不支持算术运算
  2. 一个指针类型的值不能被随意转换为另一个指针类型(必须要底层类型一致)
  3. 一个指针值不能随便其它任一指针类型的值进行比较
  4. 一个指针值不能被赋值给其它任意类型的指针值

总结

值类型: 基本数据类型int系列,float系列,bool,string,数组和结构体struct

引用类型: 指针,slice切片,map,管道chan,interface等

这两者的区别是,值传递相当于是复制了一份。而引用传递,是复制了相同的指针地址。

image.png

引用外部资料

www.topgoer.com/

gfw.go101.org/article/str…

www.kancloud.cn/hiyang/go