Go语言基础-基础语法 | 青训营笔记

379 阅读6分钟

这是我参与「第五届青训营 」笔记创作活动的第1天

本堂课重点内容

  • go语言的特点
  • go语言的开发环境
  • 基础语法以及相对应的标准库

具体案例

fmt包

fmt包实现了类似C语言printf和scanf的格式化I/O。主要分为向外输出内容和获取输入内容两大部分。

Print

Print系列函数会将内容输出到系统的标准输出,区别在于Print函数直接输出内容,Printf函数支持格式化输出字符串,Println函数会在输出内容的结尾添加一个换行符。

func main() {
 fmt.Print("在终端打印该信息。")
 name := "枯藤"
 fmt.Printf("我是:%s\n", name)
 fmt.Println("在终端打印单独一行显示")
}

Scanf

  • Scanf从标准输入扫描文本,根据format参数指定的格式去读取由空白符分隔的值保存到传递给本函数的参数中。
  • 本函数返回成功扫描的数据个数和遇到的任何错误。

变量

变量(Variable)的功能是存储数据。不同的变量保存的数据类型可能会不一样。经过半个多世纪的发展,编程语言已经基本形成了一套固定的类型,常见变量的数据类型有:整型、浮点型、布尔型等。

变量的声明

var (
     a string
     b int
     c bool
     d float32
 )//批量声明
var 变量名 类型 = 表达式 //标准声明
var 变量名1,变量名2 = 表达式1,表达式2  //标准声明
变量 := 表达式  //短声明
x, _ := foo() //匿名声明

常量

相对于变量,常量是恒定不变的值,多用于定义程序运行期间不会改变的那些值。 常量的声明和变量声明非常类似,只是把var换成了const,常量在定义的时候必须赋值。

const (
     pi = 3.1415
     e = 2.7182
 )

if else

  • 可省略条件表达式括号。
  • 持初始化语句,可定义代码块局部变量。
  • 代码块左 括号必须在条件表达式尾部。
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
} else {
/* 在布尔表达式为 false 时执行 */
}

循环

三种循环方式

s := "abc"

for i, n := 0, len(s); i < n; i++ { // 常见的 for 循环,支持初始化语句。
 println(s[i])
}

n := len(s)
for n > 0 {                // 替代 while (n > 0) {}
 println(s[n])        // 替代 for (; n > 0;) {}
 n-- 
}

for {                    // 替代 while (true) {}
 println(s)            // 替代 for (;;) {}
}

Swith

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

注意:swith中的每一个分支不需要break

数组

  1. 数组:是同一种数据类型的固定长度的序列。
  2. 数组定义:var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。
  3. 长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型。
  4. 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1 for i := 0; i < len(a); i++ { } for index, v := range a { }
  5. 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
  6. 数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。
  7. 支持 "=="、"!=" 操作符,因为内存总是被初始化过的。
  8. 指针数组 [n]*T,数组指针 *[n]T。
全局:
 var arr0 [5]int = [5]int{1, 2, 3}
 var arr1 = [5]int{1, 2, 3, 4, 5}
 var arr2 = [...]int{1, 2, 3, 4, 5, 6}
 var str = [5]string{3: "hello world", 4: "tom"}
 局部:
 a := [3]int{1, 2}           // 未初始化元素值为 0。
 b := [...]int{1, 2, 3, 4}   // 通过初始化值确定数组长度。
 c := [5]int{2: 100, 4: 200} // 使用索引号初始化元素。
 d := [...]struct {
     name string
     age  uint8
 }{
     {"user1", 10}, // 可省略元素类型。
     {"user2", 20}, // 别忘了最后一行的逗号。
 }
 遍历
 for k1, v1 := range f {
     fmt.Printf("(%d,%d)", k1, v1)
 }

注意:数组的底层是指针,因此需要注意&

切片

它通过内部指针和相关属性引用数组片段,以实现变长方案。

  • 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递,用一个框框住一块连续的内存。
  • 切片的长度可以改变,因此,切片是一个可变的数组。
  • 切片遍历方式和数组一样,可以用len()求长度。表示可用元素数量,读写操作不能超过该限制。
  • cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中array是slice引用的数组。
  • 切片的定义:var 变量名 []类型,比如 var str []string var arr []int。
//1.声明切片
var s1 []int
if s1 == nil {
   fmt.Println("是空")
} else {
   fmt.Println("不是空")
}
// 2.:=
s2 := []int{}
// 3.make()
var s3 []int = make([]int, 0)
fmt.Println(s1, s2, s3)
// 4.初始化赋值
var s4 []int = make([]int, 0, 0)
fmt.Println(s4)
s5 := []int{1, 2, 3}
fmt.Println(s5)
// 5.从数组切片
arr := [5]int{1, 2, 3, 4, 5}
var s6 []int
// 前包后不包
s6 = arr[1:4]
fmt.Println(s6)
// 切片追加
var a = []int{1, 2, 3}
 fmt.Printf("slice a : %v\n", a)
 var b = []int{4, 5, 6}
 fmt.Printf("slice b : %v\n", b)
 c := append(a, b...)//...代表拆开,一个一个添加
 fmt.Printf("slice c : %v\n", c)
 d := append(c, 7)
 fmt.Printf("slice d : %v\n", d)
 e := append(d, 8, 9, 10)
 fmt.Printf("slice e : %v\n", e)

注意:读写操作实际目标是底层数组,只需注意索引号的差别。

map

map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。 map类型的变量默认初始值为nil,需要使用make()函数来分配内存。语法为:

make(map[KeyType]ValueType, [cap])

range

range 关键字用于 for 循环中迭代数组、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。

        //使用range去求一个slice的和。使用数组跟这个很类似
	nums := []int{2, 3, 4}
	sum := 0
	for _, num := range nums {
		sum += num
	}
	fmt.Println("sum:", sum)
	//在数组上使用range将传入index和值两个变量。上面那个例子我们不需要使用该元素的序号,所以我们使用空白符"_"省略了。有时侯我们确实需要知道它的索引。
	for i, num := range nums {
		if num == 3 {
			fmt.Println("index:", i)
		}
	}
	//range也可以用在map的键值对上。
	kvs := map[string]string{"a": "apple", "b": "banana"}
	for k, v := range kvs {
		fmt.Printf("%s -> %s\n", k, v)
	}
	//range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
	for i, c := range "go" {
		fmt.Println(i, c)
	}

函数

函数是基本的代码块,用于执行一个任务。 Go 语言最少有1个 main() 函数。 Go 语言函数定义格式如下:

func function_name( [parameter list] ) [return_types] {
   函数体
}

函数定义解析:

  • func:函数由 func 开始声明
  • function_name:函数名称,参数列表和返回值类型构成了函数签名。
  • parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
  • return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
  • 函数体:函数定义的代码集合。
func max(num1, num2 int) int {  
   /* 定义局部变量 */  
   var result int  
  
   if (num1 > num2) {  
      result = num1  
   } else {  
      result = num2  
   }  
   return result  
}

指针

Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。类型指针不能进行偏移和运算。Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)。

ptr := &v    // v的类型为T
v:代表被取地址的变量,类型为T
ptr:用于接收地址的变量,ptr的类型就为*T,称做T的指针类型。*代表指针。

注意:

  1. 对变量进行取地址(&)操作,可以获得这个变量的指针变量。
  2. 指针变量的值是指针地址。
  3. 对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。

结构体

Go语言中通过struct来实现面向对象,用来描述一组值的。

 type 类型名 struct {
     字段名 字段类型
     字段名 字段类型
     …
 }
 1.类型名:标识自定义结构体的名称,在同一个包内不能重复。
 2.字段名:表示结构体字段名。结构体中的字段名必须唯一。
 3.字段类型:表示结构体字段的具体类型。
 4.结构体中字段大写开头表示可公开访问,小写表示私有(仅在定义当前结构体的包中可访问)。

Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)。接收者的概念就类似于其他语言中的this或者 self。

//指针类型
func (p *Person) SetAge(newAge int8) {
     p.age = newAge
 }
 func main() {
 p1 := NewPerson("测试", 25)
 fmt.Println(p1.age) // 25
 p1.SetAge(30)
 fmt.Println(p1.age) // 30
}
//值类型
func (p Person) SetAge2(newAge int8) {
 p.age = newAge
}

func main() {
 p1 := NewPerson("测试", 25)
 p1.Dream()
 fmt.Println(p1.age) // 25
 p1.SetAge2(30) // (*p1).SetAge2(30)
 fmt.Println(p1.age) // 25
}

错误处理


func main() {  
    f, err := os.Open("/test.txt")
    if err != nil {
        fmt.Println("error:",err)
        return
    }
    fmt.Println(f.Name(), "open successfully")
}

字符串

a := "hello"
fmt.Println(strings. Contains(a,"ll"))
fmt.Println( strings. Count(a,"l"))
fmt.Println( strings.HasPrefix( a,"he" ) )
fmt.Println(strings.HasSuffix( a,"llo") )
fmt.Println(strings. Index( a,"ll"))
fmt.Println( strings.Join([]string{ "he","llo"},"-" ))
fmt.Println(strings. Repeat(a,2))
fmt.Println( strings.Replace(a,"e","E"-1))
fmt.Println(strings.Split( "a-b-c " , "-"))
fmt.Println(strings . ToLower( a ) )
fmt .Println( strings.ToUpper( a ) )
fmt.Println( len(a ) )
b :=“你好"
fmt.Println( len(b ) )
//字符串的格式化输出
fmt.Printf( " s=%v\n" ,s )// s=hello
fmt.Printf( "n=%v\n", n) / / n=123
fmt.Printf( " p=%vn",p)/ / p={1 2}
fmt.Printf( " p=%+v\n" ,p)/ /p=[x :l y:2}
fmt.Printf( " p=%#v\n" ,p)// p=main.point{x:l, y:2]

个人总结

经过这一天的学习,我大致了解了基础的语法,但是老师讲得知识点没有很详细,我们需要自己了解更多。