go语言基础语法学习笔记| 青训营

187 阅读7分钟

go语言基础语法

0.基本目标

  • 熟悉相关的Go语言基础语法
  • 了解slice,map的部分底层原理
  • 完成相关代码的自己实现

1.变量

go语言当中变量声明的基本格式是

var 变量名 变量类型

常用的几种的变量声明方式如下: ``

func Variable_tests() {  
var a = "hello,world!" //自动推导变量类型 var 变量=value  
var b, c int = 1, 2 //显式说明变量类型  
var d = true  
var e float64  
f := float32(e)  
g := a + "foo"  
fmt.Println(a, b, c, d, e, f, g)  
  
const s string = "constant"  
const h = 300000  
const i = 3e20 / h  
fmt.Println(s, h, i, math.Sin(h), math.Sin(i))  
}

关于变量我觉得需要注意的点在于匿名变量,这部分我们没有介绍,但是后面的例子上面用到了。

使用多重赋值时,如果不需要在左值中接受变量,可以使用匿名变量

例如

t3, _ := time.Parse("2006-02-02 13:14:15", "2023-01-02 13:14:15")

“_”本身就是一个特殊的标识符,被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。 上面的这个例子用来处理了error,但是本质是错误的。正常情况下,我们对于error一定要进行处理的,不可以将其舍弃。

2.流程控制

这部分包括if-else,for,switch和for range这四部分的课程内容相互对应。

2.1 if-else

go语言的if-else没有括号,即使你加上,编译器也会自动给你去掉。其次 go语言的if-else要求必须要加上{}

if conditions {
    // 条件为真执行
}
else{
//否则实现else
}

这是其基础语法表现形式。 其中if-else彼此之间也可以互相之间嵌套 下面是我实现老师课程所讲内容代码。

 
  
func control_gramer() {  
if 7%2 == 0 {  
fmt.Println("7 is even")  
} else {  
fmt.Println("7 is odd")  
}  
  
if 8%4 == 0 {  
fmt.Println("8 is divided by 4")  
}  
if num := 9; num < 0 {  
fmt.Println(num, "is negative")  
} else if num < 10 {  
fmt.Println(num, "has a digit")  
} else {  
fmt.Println(num, "has multiple digit")  
}  
}

2.2 go语言的for循环

for循环的基本结构


for i := 0; i < 10; i++ {
    //for循环内容
}

go语言抛弃了while和do-while循环只保存了一种形式,即for循环形式。 本质算法层面for循环和while和do-while也可以进行等价,三者彼此可以互相转换。详细一些底层实现可以看一下这篇文章从汇编角度看三种循环结构 关于for循环相关主要代码实现:

 
  
func ForTests() {  
i := 1  
for {  
fmt.Println("loop")  
break  
}  
for j := 7; j < 9; j++ {  
fmt.Println(j)  
}  
for n := 0; n < 5; n++ {  
if n%2 == 0 {  
continue  
}  
fmt.Println(n)  
}  
for i <= 3 {  
fmt.Println(i)  
i++  
}  
}

接下来是一些探究内容: 对于go语言的for循环,跳出方式有四种。

  1. return语句
  2. break
  3. goto
  4. panic 对于1,我们运行到return语句就会退出,对于2运行到break会跳出循环,对于3可以goto到程序任何设置的位置,不过由于goto对于系统维护性具有较大损失,所以不建议使用。最后是panic,报错之后直接结束。

2.3 switch循环

switch循环语句,不是任天堂主机(狗头)。 go语言的switch和if类似没有括号,并且switch 分支表达式可以是任意类型,不限于常量。可省略 break,默认自动终止。其执行顺序从上到下依次执行,直到退出。 其基础语法如下:

switch var1 {
    case val1:
        ...
    case val2:
        ...
    default:
        ...
}

这里所说的默认自动终止,如果有一个case的实现了程序自动运行break跳出。如果我们想要接着运行下一个case,我们使用fallthrough。无论下个条件是否为真,我们都会执行。 下面是我实现的相关代码:


  
func SwitchTest() {  
a := 2  
switch a {  
case 1:  
fmt.Println("one")  
case 2:  
fmt.Println("two")  
case 3:  
fmt.Println("three")  
case 4, 5:  
fmt.Println("four or five")  
default:  
  
fmt.Println("other")  
}  
t := time.Now()  
switch {  
case t.Hour() < 12:  
println("It is before noon")  
default:  
println("It is after noon")  
}  
}

2.4 for-range

限于老师课上时间,这里没有介绍这个我们也是经常使用的一个语法for-range,其基本形式如下:


for key, val := range data {
    ...
}

这个由于在日常开发过程中,我们经常用来遍历数组,map,channel,用的非常频繁。后面介绍复合数据类型的时候重点介绍。

3.复合数据类型

3.1数组

数组是一个由固定长度的特定类型元素组成的序列,其长度可为0个元素或者多个元素。由于实际开发过程中,并不容易事先确定数组长度,因此我们使用slice更多一些。 go语言数组声明的基本形式如下:

var 数组变量名 [元素数量]Type

下面是我的一些实现:

unc ArrayTest() {  
var a [5]int  //声明数组
a[4] = 100  //赋值
fmt.Println(a[4], len(a))  
  
b := [5]int{1, 2, 3, 4, 5} //同时进行声明和赋值

fmt.Println(b)  
  
var TwoD [2][3]int  //声明多维数组
for i := 0; i < 2; i++ {  
for j := 0; j < 3; j++ {  
TwoD[i][j] = i + j  //对多维数组进行赋值
}  
}  
fmt.Println("2d", TwoD)  
}

3.2切片

Slice(切片)代表变长的序列,序列中每个元素都有相同的类型。一个slice类型一般写作[]T,其中T代表slice中元素的类型;slice的语法和数组很像,只是没有固定长度而已。

上面的这段来自go语言圣经的定义介绍了一下什么是切片。Go语言中切片的内部结构包含地址大小容量,切片一般用于快速地操作一块数据集合。 要注意的是slice的第一个元素并不一定就是数组的第一个元素。 多个slice可能会共享同一个底层数据,并且引用的底层数组可能重合。 举个简单的例子,一年12个月,底层是12个月的数组,我们分别取3-9月和6到12月,这两个切片共用了同一个底层数据6到9月。

长度对应slice中元素的数目;数组的长度不能超过容量,容量一般是从slice的开始位置到底层数据的结尾位置。其中内置的len和cap函数分别返回slice的长度和容量,长度与容量代表两个不同含义。举个简单例子,一个房子能住100人,容量为100,实际住了20人,长度为20。

slice的基本格式如下:

//name 表示切片的变量名,Type 表示切片对应的元素类型。
var name []Type

下面是我代码的一些实现

func SliceTests() {  
fmt.Println("切片相关测试")  
s := make([]string, 3)  
s[0] = "a"  
s[1] = "b"  
s[2] = "c"  
fmt.Println("get", s[2])  
fmt.Println("len", len(s))  
  
s = append(s, "d")  
s = append(s, "e", "f")  
fmt.Println(s)  
  
c := make([]string, len(s))  
copy(c, s)  
fmt.Println(c)  
  
fmt.Println(s[2:5])  
fmt.Println(s[2:])  
fmt.Println(s[:5])  
  
good := []string{"g", "o", "o", "d"}  
fmt.Println(good)  
  
}

slice和数组的区别:

  • 数组拥有固定长度,而slice没有
  • 数组可以直接使用==比较(不建议),slice使用bytes.Equal函数来判断两个字节型slice是否相等([]byte)。对于别的类型需要一一展开进行比较。

下面简单说一下slice的扩容: 每次调用append函数的时候,对于slice进行的检测,如果 底层数组拥有的的容量足够容纳新来的元素,那么将新加入的元素复制到拓展的空间,依然还在底层数组。

但是如果不足以容纳,会将以当前slice的容量的2倍开辟新区域,接着将原数组内容copy到新数组,并接下来复制append的元素。 这里需要注意的是,原数组这部分的数据使用的底层数组已经发生了改变。

3.3MAP

map 是一种无序的键值对的集合。

map 是引用类型,在我们日常工作过程中,使用的非常多,其基本声明格式:


varname map[keytype] valuetype

下面是我的一些map的代码实现

func maptests() {  
fmt.Println("哈希表的使用")  
m := make(map[string]int)  
m["one"] = 1  
m["two"] = 2  
m["three"] = 3  
fmt.Println(m)  
fmt.Println(len(m))  
fmt.Println(m["one"])  
fmt.Println(m["four"])  
  
r, ok := m["four"]  
println(r, ok)  
  
delete(m, "one")  
m2 := map[string]int{"one": 1, "two": 2}  
var m3 = map[string]int{"one": 1, "two": 2}  
println(m2, m3)  
}

map的key需要不重复,但是没有提供set,因此map使用slice,例如通过将 value 定义为 []int 类型或者其他类型的切片。

3.4结构体

结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员。 结构体的定义格式如下:


type 类型名 struct {
    字段1 字段1类型
    字段2 字段2类型
    
}

下面是我关于结构体部分的代码实现:

type user struct { 
id int
name string  
password string  
}

结构体成员的输入顺序也有重要的意义,如果元素定义的先后顺序不同,在程序中会被认作不同的结构体。

3.5JSON

JavaScript对象表示法(JSON)是一种用于发送和接收结构化信息的标准协议。

相比JSON,Protocol Buffers在微服务框架中广泛使用,YAML对于人来说较为直观清晰,JSON对于机器来说很好读取,XML对于以上都不好读取(狗头)。 但是由于简洁性、可读性和流行程度等原因,JSON是应用最广泛的一个。

go语言里面的JSON 操作非常简单,对于一个已有的结构体,我们可以什么都不做,只要保证每个字段的第一个字母是大写,也就是是公开字段。那么这个结构体就能用 JSON.marshaler 去序列化,变成一个JSON的字符串。 序列化之后的字符串也能够用 JSON.unmarshaler 去反序列化到一个空的变量里面。

这样默认序列化出来的字符串的话,它的风格是大写字母开头,而不是下划线。我们可以在后面用json tag 等语法来去修改输出 JSON 结果里面的字段名。

下面是我关于JSON的相关代码实现:

type userInfo struct {  
Name string  
Age int  
Hobby []string  
}  
  
func jsontests() {  
a := userInfo{"wang", 18, []string{"golang", "java"}}  
buf, err := json.Marshal(a)  
if err != nil {  
panic(err)  
}  
fmt.Println(buf) //输出字节流  
fmt.Println(string(buf))  
  
buf, err = json.MarshalIndent(a, "", "\t")  
if err != nil {  
panic(err)  
}  
fmt.Println(buf) //输出字节流  
fmt.Println(string(buf))  
  
var b userInfo  
err = json.Unmarshal(buf, &b)  
if err != nil {  
panic(err)  
}  
fmt.Printf("%v\n", b)  
  
}

4.总结

上面简单介绍了go语言的变量,流程控制和常见的复合数据类型的一篇技术学习总结。希望可以帮到大家。

参考相关文章

从汇编角度看三种循环结构

go语言圣经