go 语言学习笔记

52 阅读4分钟

程序结构

类型

类型定义

type 类型名字 底层类型

类型的应用场景 ?

作用域 ?

一个声明语句将程序中的实体和一个名字关联,比如一个函数或一个变量。声明语句的作用域是指源代码中可以有效使用这个名字的范围。

声明语句的作用域对应的是一个源代码的文本区域;它是一个编译时的属性。

变量生命周期 ?

变量的生命周期指的是在程序运行期间变量有效存在的时间段

一个变量的生命周期是指程序运行时变量存在的有效时间段,在此时间区域内它可以被程序的其他部分引用;是一个运行时的概念。

基础数据类型

整型

  • int
  • int8
  • int16
  • int32
  • int64
  • uint
  • uint8
  • uint16
  • uint32
  • uint64
  • uintptr

浮点

  • float32

  • float64

复数

  • complex64

  • complex128

布尔类型

  • bool

字符串

一个字符串是一个不可改变的字节序列

常量

复合数据类型

数组

数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,因此在Go语言中很少直接使用数组

package main

import (
   "fmt"
)

func main() {

   //定义
   var a [10]int
   var b [5]int
   fmt.Printf("len(a)=%d,len(b)=%d\n", len(a), len(b))

   //赋值
   for i := 0; i < len(a); i++ {
      a[i] = i + 1
   }

   //遍历
   for i, data := range a {
      fmt.Printf("a[%d] = %d\n", i, data)
   }

   //赋值并初始化
   c := [3]int{1, 2}           //未初始化的值为 0
   d := [...]int{1, 2, 3}      //通过初始化值确定数据长度
   e := [5]int{2: 100, 4: 200} //通过索引好初始化元素,

   fmt.Println(c, d, e)

   //update,数组是值类型,每次传递都将产生一份副本
   fmt.Println("test modify")
   f := [5]int{1, 2, 3, 4, 5}
   fmt.Println("before modify", f) //before modify [1 2 3 4 5]
   modify(f)
   fmt.Println("after modify", f) //after modify [1 2 3 4 5]
}

func modify(array [5]int) {
   array[0] = 10
   fmt.Println("i am modify,array values:", array) //i am modify,array values: [10 2 3 4 5]
}

切片

数组的长度在定义之后无法再次修改;数组是值类型,每次传递都将产生一份副本。显然这种数据结构无法完全满足开发者的真实需求。Go语言提供了数组切片(slice)来弥补数组的不足。

切片并不是数组或数组指针,它通过内部指针和相关属性引⽤数组⽚段,以实现变⻓⽅案。

slice并不是真正意义上的动态数组,而是一个引用类型。slice总是指向一个底层array,slice的声明也可以像array一样,只是不需要长度。

map

Go语言中的map(映射、字典)是一种内置的数据结构,它是一个无序的key—value对的集合

package main

import (
   "fmt"
)

// 在函数间传递映射并不会制造出该映射的一个副本,不是值传递,而是引用传递:
func deleteMap(m map[int]string, key int) {
   delete(m, key)
   for k, v := range m {
      fmt.Printf("deleteMap---->%d ----> %s \n", k, v)
   }
}

func main() {
   var m1 map[int]string
   fmt.Println(m1 == nil)

   m2 := map[int]string{}
   m3 := make(map[int]string)
   fmt.Println(m2, m3)

   //定义同时初始化
   var m4 map[int]string = map[int]string{1: "mike", 2: "yoyo"}
   fmt.Println(m4)

   //赋值
   var m5 map[int]string = map[int]string{1: "mike", 2: "yoyo"}
   m5[1] = "xxx"
   m5[2] = "lily"
   fmt.Println("m5=", m5)

   //遍历
   for k, v := range m5 {
      fmt.Printf("%d ----> %s \n", k, v)
   }

   //遍历
   for k := range m5 {
      fmt.Printf("%d ----> %s \n", k, m5[k])
   }

   //删除
   //delete(m5, 2)
   //for k := range m5 {
   // fmt.Printf("%d ----> %s \n", k, m5[k])
   //}

   deleteMap(m5, 2)
   for k := range m5 {
      fmt.Printf("ater deleteMap------->%d ----> %s \n", k, m5[k])
   }

}

结构体

有时我们需要将不同类型的数据组合成一个有机的整体,如:一个学生有学号/姓名/性别/年龄/地址等属性。显然单独定义以上变量比较繁琐,数据不便于管理。

结构体是一种聚合的数据类型,它是由一系列具有相同类型或不同类型的数据构成的数据集合。每个数据称为结构体的成员

package main

import "fmt"

type Student struct {
   id   int
   name string
   sex  byte
   age  int
   addr string
}

//值传递,不改结构体内容
func printStudentValue(tmp Student) {
   tmp.name = "值传递"
   fmt.Println("printStudentValue temp=", tmp)
}

func printStudentPointer(p *Student) {
   p.name = "引用传递"
   fmt.Println("printStudentPointer p=", p)
}

func main() {
   //普通变量
   //顺序初始化,必须每个成员都初始化
   var s1 Student = Student{1, "mike", 'm', 18, "sz"}
   s2 := Student{2, "yoyo", 'f', 20, "sz"}
   fmt.Println("s1=", s1)
   fmt.Println("s2=", s2)

   //指定初始化某个成员,没有初始化的成员未零值
   s3 := Student{id: 2, name: "lili"}
   fmt.Println("s3=", s3)

   //指针变量
   s4 := &Student{4, "coco", 'm', 3, "sh"}
   fmt.Println("s4=", *s4)

   //打印成员
   s5 := Student{2, "yoyo", 'f', 20, "sz"}
   fmt.Printf("id = %d, name= %s, sex=%c, age=%d ,addr=%s\n", s5.id, s5.name, s5.sex, s5.age, s5.addr)

   //成员变量赋值
   var s6 Student
   s6.id = 2
   s6.name = "yoyo"
   s6.sex = 'f'
   s6.age = 16
   s6.addr = "guangzhou"
   fmt.Println("s6=", s6)

   //指针变量
   s7 := new(Student)
   s7.id = 3
   s7.name = "xxx"
   fmt.Println("s7=", s7)

   //普通变量和指针变量类型打印
   var s8 Student = Student{4, "uuu", 'm', 18, "sz"}
   fmt.Printf("s8= %v , &s8 = %v\n", s8, &s8)

   //指针变量赋值
   var s9 *Student = &s8
   s9.id = 10
   (*s9).name = "zzz"
   fmt.Println(s9, *s9, s8)

   //结构体比较
   fmt.Println("s1 == s2", s1 == s2)

   //值传递
   var s10 Student = Student{4, "uuu", 'm', 18, "sz"}
   fmt.Println("before printStudentValue s10=", s10)
   printStudentValue(s10)
   fmt.Println("after printStudentValue s10=", s10)

   //引用类型传递
   var s11 Student = Student{4, "uuu", 'm', 18, "sz"}
   fmt.Println("before printStudentPointer s10=", s11)
   printStudentPointer(&s11)
   fmt.Println("after printStudentPointer s10=", s11)

}

可见性

Go语言对关键字的增加非常吝啬,其中没有private、 protected、 public这样的关键字。

要使某个符号对其他包(package)可见(即可以访问),需要将该符号定义为以大写字母
开头。

函数

函数可以让我们将一个语句序列打包为一个单元,然后可以从程序中其它地方多次调用。函数的机制可以让我们将一个大的工作分解为小的任务,这样的小任务可以让不同程序员在不同时间、不同地方独立完成。一个函数同时对用户隐藏了其实现细节

函数声明

函数声明包括函数名、形式参数列表、返回值列表(可省略)以及函数体。

func name(parameter-list) (result-list) {
    body
}
package main

import "fmt"

func add(x int, y int) int {
   return x + y
}

func sub(x, y int) (z int) {
   z = x - y
   return z
}

func first(x int, _ int) int {
   return x
}

func zero1(int, int) int {
   return 0
}

func main() {
   var addResult = add(3, 5)
   fmt.Printf("%T\n", add)   //func(int, int) int
   fmt.Printf("%T\n", sub)   //func(int, int) int
   fmt.Printf("%T\n", first) //func(int, int) int
   fmt.Printf("%T\n", zero1) //func(int, int) int

   fmt.Printf("%d\n", addResult)

}
  • 实参通过值的方式传递,因此函数的形参是实参的拷贝。对形参进行修改不会影响实参。但是,如果实参包括引用类型,如指针,slice(切片)、map、function、channel等类型,实参可能会由于函数的间接引用被修改。