Go语言笔记

本文详细介绍了Go语言的基础语法,包括变量、常量、数据类型、函数、数组、切片、结构体、指针、通道和并发等核心概念。通过示例代码展示了如何在Go中声明、初始化、操作数据,以及如何实现简单的并发操作。此外,还讲解了Go的包管理和访问控制原则,以及函数作为参数和返回值的用法。文章最后讨论了Go的错误处理和类型转换,帮助读者全面理解Go语言的基本编程技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/**
标识符(常量、变量、类型名、函数、结构字段等)以一个大写字母开头,如:Group 1,可以被外部包的代码使用(客户端使用,需要先导入这个这个包)
相当于面向对象的public;标识符以小写字母开头,则对包外不开见,相当于面向对象语言的protected,但是他们在整个包里是是可见并且被使用的。

 */
package main   /* 定义包名 每个go程序都有唯一的一个main包--》可独立执行的程序 */
import "fmt"

// import "fmt"	// 引入fmt包(使用其中的函数或者元素)fmt包实现了格式化io的函数

func main(){
	/* 第一个简单程序 */
	fmt.Println("Hello world!!!")
	/* Println(后默认加换行)和Print支持使用变量 */
} 

go基础语法

/**
go的基础语法:
	行分隔符:没有(不像C需要分号)每行是一个语句,多个语句写到一行需要使用";"分隔
	标识符:标识符(常量、变量、类型名、函数、结构字段等)以一个大写字母开头,如:Group 1,
		可以被外部包的代码使用(客户端使用,需要先导入这个这个包)相当于面向对象的public;
		标识符以小写字母开头,则对包外不开见,相当于面向对象语言的protected,但是他们在整个包里是是可见并且被使用的。
	字符串连接:+
	关键字:
	格式化字符串:使用Sprintf()字符串格式化输出

*/
package main

import (
	"fmt"
)

func main() {
	// %d 表示整型数字 %s表示字符串
	var stockcode = 123
	var enddate = "2021-9-20"
	var url = "Code = %d&endDate = %s"
	var target_url = fmt.Sprintf(url, stockcode, enddate)
	fmt.Printf(target_url)
}

注意⚠️:"{ " 或者 " }"不可以单独一行

// 以下运行报错
package main

func main()
{												// 	"{"不可以单独一行
	fmt.Println("Hello World!")
}

go数据类型

go语言数据类型:
    1、布尔类型
			var b bool = true
    2、数字类型
        整型int和浮点型float32float64、还支持复数
				有符号整型
        int8
        int16
        int32
        int64
				无符号整型
        uint8
        uint16
        uint32
        uint64
        浮点型
        float32
        float64
        complex64   // 32位实数和虚数
        complex128   // 64位实数和虚数
        # 其他数字类型
        byte
        uintptr(un int pointer) // 无符号整型,用于存放一个指针
    3、字符串类型(string4、派生类型
        a、指针(Pointer)
        b、数组
        c、结构化类型(struct)
        d、Channel类型			// chan类型,类似于一个数组。
				/**
				<-chan 是对chan中的数据读取
				chan<-value 是对chan赋值
				*/
        e、函数类型
        f、切片类型
        g、接口类型(interface)
        h、Map类型

channel数据类型(管道)

通过chan数据类型,并发核心单元可以发送和接受数据进行通讯(communication)

操作符是:“<-”,箭头的方向是数据的流向。

var ch chan int
ch <- v  // 发送v到channel ch中
v := <- ch  // 从channel ch中接受数据,并将数据赋值给v

go语言变量

// go语言变量
// 声明和定义的区别:声明没有分配内存,定义创建了变量,并为其分配了内存空间;一个变量在一定区域内只能被定义一次,但是可以被声明多次。
/**
声明变量用var或者变量声明语句:“:=”


变量声明
1、第一种,指定变量类型,如果没有初始化,变量默认为零值
2、第二种,不指定变量类型,根据变量值,自行判断值类型
3、第三种,使用变量声明语句“ := ”
*/
package main
import (
	"fmt"
)

var (	// 这种因式分解关键字的形式,一般用于声明全局变量
		z int
		m string
	)
	
func main(){
	// 声明一个变量,并初始化
	var a string = "Runnoob"
	fmt.Println(a)	// Runnoob

	// 多变量声明
	var b, c = 1, 2
	fmt.Println(b, c)	// 1 2
	// 类型相同的多个变量,不是全局变量
	/**
	var vname1, vname2, vname3 string
	vname1, vname2, vname3 = "v1", "v2", "v3"

	var vname1, vname2, vname3 = v1, v2, v3   // 和python相同,不需要声明类型,自动推断
	vname1, vname2, vname3 := v1, v2, v3  // :=左侧的变量不应该是被声明过的,要么会报错
	*/
	var vname1, vname2, vname3 = 1, 2, 123
	fmt.Println(vname3, vname2, vname1)		// 123 2 1

	var v1, v2, v3 int
	v1, v2, v3 = 1, 2, 3
	fmt.Println(v1, v2, v3)	// 1 2 3

	

	// 第一种变量声明:指定变量类型,如果没有初始化,变量默认为0值
	var x int
	fmt.Println(x)	// 0

	// 第二种变量声明:不指定变量类型,根据变量值,自行判断变量值
	var y = 123
	fmt.Println(y)	// 123

	// 第三种变量声明:使用变量声明语句“ := ”
	intVal := 1  // var intVal int = 1 不带声明的格式用于函数体内
	fmt.Println(intVal)	// 1
	floatVal := 3.14	// var floatVal float = 3.14
	fmt.Println(floatVal)	// 3.14
	f := "Runnoob"	// var f string = "Runnoob"
	fmt.Println(f)	// Runnoob
}

// 值类型和引用类型
/**
int, float, bool, string是值类型,使用这些类型的变量都指向存在内存中的值

使用"="进行赋值的变量,实质上是将内存中的值进行了拷贝,
*/

语言常量

package main

import "fmt"

func main() {
	const LENGTH int = 10
	const WIDTH int = 5
	var area int
	const a, b, c = 1, false, "str"	  // 多重赋值

	area = LENGTH * WIDTH
	fmt.Printf("面积为:%d", area)
	println()		// 换行
	println(a, b, c)
}




package main

import "unsafe"

func main() {
	// 常量也可以使用枚举
	const(
		a = 0
		b = 1
		c = 2
		d = "abc"
		e = len(d)
		f = unsafe.Sizeof(d)
	)
	println(a, b, c)	// 0, 1, 2
	println(d, e, f)	// abc 3 16

}


// iota特殊常量(表示从0开始自动加1)

package main

func main(){
	const (
	a = iota
	b
	c = "haha"
	d
	e = 100
	f
	h = iota
	g
	)
	println(a, b, c, d, e, f, h, g)	//  0 1 haha haha 100 100 6 7
}

条件语句

if语句

package main

import "fmt"

func main() {
	var a int = 10

	if a < 20 {
		fmt.Println("a小于20") // a小于20
	}
	fmt.Printf("a的值为:%d\n", a)		// a的值为:10
}

if…else…语句

// if...else语句
package main

import "fmt"

func main() {
	var a int = 10
	if a < 20 {
		fmt.Printf("a小于20\n")	
	}else {
		fmt.Printf("a大于20\n")
	}
}
// a小于20

switch语句

// switch语句
package main

import "fmt"

func main() {
	var grade string = "B"
	var marks int
	fmt.Scanf("%d", &marks)
	switch marks {
	case 90:
		grade = "A"
	case 80:
		grade = "B"
	case 50, 60, 70:
		grade = "C"
	default:
		grade = "D"
	}
	switch {
	case grade == "A":
		fmt.Printf("优秀\n")
	case grade == "B", grade == "C":
		fmt.Printf("良好\n")
	case grade == "D":
		fmt.Printf("及格\n")
	case grade == "F":
		fmt.Printf("不及格\n")
	default:
		fmt.Printf("差\n")
	}
	fmt.Printf("您的等级为:%s\n", grade)
}

//70
//良好
//您的等级为:C
//
//Process finished with the exit code 0

select语句

select类似用于通信的switch语句

package main

import "fmt"

func main() {
	var c1, c2, c3 chan int
	var i1, i2 int
	select {
	case i1 = <-c1:		// 从channel c1中接收数据并将数据赋值给i1
		fmt.Printf("received", i1, "from c1\n")
	case c2 <- i2:		// 发送i2到channel c2
		fmt.Printf("sent", i2, "to c2\n")
	case i3, ok := (<-c3):
		if ok {
			fmt.Printf("received", i3, "from c3\n")
		} else {
			fmt.Printf("c3 is closed\n")
		}
	default:
		fmt.Printf("no communication\n")
	}
}

循环语句

for循环

// 计算1 to 10的数字之和:
package main

import "fmt"

func main() {
	sum := 0
	for i := 0; i <= 10; i++ {
		sum += i
	}
	fmt.Println(sum)		// 55
}

// 计算sum小于10的时候sum自相加后的值
package main

import "fmt"

func main() {
	sum := 1
	for ; sum <= 10; {
		sum += sum
	}
	fmt.Println(sum)	// 16
}

// For-each range循环
// 这种格式可以对字符串、数组、切片等进行迭代输出元素
package main

import "fmt"

func main() {
	strings := []string{"google", "runoob"}
	for i, s := range strings{
		fmt.Println(i, s)
	}

	numbers := [6]int{1, 2, 3, 5}
	for i, x := range numbers {
		fmt.Printf("第 %d 位 x 的值 = %d\n", i, x)
	}
}
/**
0 google
1 runoob
第 0 位 x 的值 = 1
第 1 位 x 的值 = 2
第 2 位 x 的值 = 3
第 3 位 x 的值 = 5
第 4 位 x 的值 = 0
第 5 位 x 的值 = 0
 */

函数

函数声明告诉编译器函数的名称、返回类型、参数。

package main

import "fmt"

func main() {
	/* 定义局部变量 */
	var a int = 100
	var b int = 200
	var ret int

	/* 调用max函数并返回最大值 */
	ret = max(a, b)

	fmt.Printf("最大值是:%d", ret)		// 最大值是:200
}

// max函数代码
// 传入两个整型参数num1和num2,并返回这两个参数的最大值
// 函数名(参数列表)返回值类型 {  }
func max(num1, num2 int) int {
	/* 声明局部变量 */
	var result int

	if (num1 > num2) {
		result = num1
    } else {
		result = num2
    }
	return result
}
// 返回多个值
package main

import "fmt"

func swap(x, y string) (string, string) {
	return y, x
}
func main() {
	a, b := swap("Google", "runoob")
	fmt.Println(a, b)	// runoob Google
}

函数参数

函数 “值传递” 值

将实际参数复制一份传递到函数中,这样在函数中对参数进行修改不会影响到实际参数。

// 值传递
// ⚠️这里交换的是内存单元,值没有交换
package main

import "fmt"

func main() {
	var a int = 100
	var b int = 200

	fmt.Printf("交换前 a 的值:%d\n", a)  // 100
	fmt.Printf("交换前 b 的值:%d\n", b)  // 200

	swap(a, b)

	fmt.Printf("交换后 a 的值:%d\n", a)  // 100
	fmt.Printf("交换后 b 的值:%d\n", b)  // 200
	// 因为使用的是值传递,因此两个值并没有实现交互
}

func swap(x, y int) int {
	var temp int

	temp = x
	x = y
	y = temp

	return temp
}

函数 “引用传递” 值

引用传递是指,函数调用时,将实际参数的地址传递到函数中,这样在函数中对参数进行修改,将影响到实际参数。(引用传递 指针参数 传递到函数内)

// 函数 引用 传递值
// ⚠️这里交换的变量值
package main

import "fmt"

func main() {
	// 定义局部变量
	var a int = 100
	var b int = 200

	fmt.Printf("交换前 a 的值:%d\n", a)  // 100
	fmt.Printf("交换前 b 的值:%d\n", b)  // 200

	swap(&a, &b)

	fmt.Printf("交换后 a 的值:%d\n", a)  // 200
	fmt.Printf("交换后 b 的值:%d\n", b)  // 100


}

func swap(x *int, y *int) {		// &a, &b将a、b的地址传递进去
	var temp int
	temp = *x		// 将a的地址指向的变量赋值给temp
	*x = *y
	*y = temp
}

函数用法

函数作为另一个函数的实参

package main

import (
	"fmt"
	"math"
)

func main() {
	// 声明函数
	getSquareRoot := func(x float64) float64 {
		return math.Sqrt(x)
	}
	
	// 使用函数 将getSquareRoot作为Println的s h
	fmt.Println(getSquareRoot(9))	// 3 
}

闭包——匿名函数

package main

import "fmt"

func getSequence() func() int {
	i := 0
	return func() int {
		i += 1
		return i
	}
}

func main() {
	// nextNumber为一个函数, 函数 i为0
	nextNumber := getSequence()

	// 调用 nextNumber 函数, i变量自增1并返回
	fmt.Println(nextNumber()) // 1
	fmt.Println(nextNumber()) // 2
	fmt.Println(nextNumber()) // 3

	// 创建新的函数 nextNumber1, 并查看结果
	nextNumber1 := getSequence()
	fmt.Println(nextNumber1()) // 1
	fmt.Println(nextNumber1()) // 2
	fmt.Println(nextNumber1()) // 3
}

函数方法

// 函数方法package mainimport (   "fmt")type Circle struct {   // 结构体类型   radius float64}func main() {   var c1 Circle   c1.radius = 10.00   fmt.Println("圆的面积 = ", c1.getArea())	// 圆的面积 = 314}// 该method属于Circle类型对象中的方法func (c Circle) getArea() float64 {   // c.radius即为Circle类型对象中的属性   return 3.14 * c.radius * c.radius}

变量作用域

局部变量:函数体内定义的

全局变量:函数体外定义的

形式参数:函数定义中的变量称为形参

局部变量

// 局部变量
package main

import "fmt"

func main() {
   /* 声明局部变量 */
   var a, b, c int

   a = 10
   b = 20
   c = a + b

   fmt.Printf("结果: a = %d, b = %d and c = %d\n ", a, b, c)
}

全局变量

// 声明全局变量
var g int
func main() {
   // 声明局部变量
   var a, b int

   a = 10
   b = 20
   g = a + b

   fmt.Printf("结果: a = %d, b = %d and g = %d\n", a, b, g)
   // 结果: a = 10, b = 20 and g = 30
}

go中的全局变量名和局部变量名可以相同(优先使用函数体内的局部变量)

// 全局变量名和局部变量名可以相同,但是会优先考虑函数体内的局部变量package mainimport "fmt"// 声明全局变量var g int = 20func main() {   // 声明局部变量   var g int = 10   fmt.Printf("结果:g = %d\n", g)  // 10}

形式参数(作为函数体内的局部变量使用)

// 形式参数package mainimport (   "fmt")// 声明全局变量var a int = 20func main() {   // main函数中声明局部变量   var a int = 10   var b int = 20   var c int = 0   fmt.Printf("main()函数中 a = %d\n", a)   c = sum(a, b)   fmt.Printf("main()函数中 c = %d\n", c)}func sum(a, b int) int { // 这里的 a, b是形参   fmt.Printf("sum() 函数中 a = %d\n", a)   fmt.Printf("sum() 函数中 b = %d\n", b)   return a + b}//main()函数中 a = 10//sum() 函数中 a = 10//sum() 函数中 b = 20//main()函数中 c = 30

数组

声明数组

var variable_name [size] variable_typevar balance [10] float32

初始化数组

var balance = [5]float32{12.0, 145.0, 13.0, 14.0, 15.0}
或
balance := [5]float32{12.0, 145.0, 13.0, 14.0, 15.0}

// 数组长度不确定 [...] 代替数组长度
var balance = [...]float32{12.0, 145.0, 13.0, 14.0, 15.0}
或
balance := [...]float32{12.0, 145.0, 13.0, 14.0, 15.0}

// 设置了数组长度,还可以通过指定下标来初始化元素
// 初始化索引为1, 3的元素值分别为2.0、7.0
balance := [5]float32{1:2.0, 3:7.0}

访问数组元素

通过索引读取数组元素

package main

import "fmt"

func main() {
	var n [10]int  // n是一个长度为10的整型数组
	var i, j int

	// 为数组n初始化元素
	for i = 0; i < 10; i++ {
		n[i] = i + 100
	}

	// 输出数组元素
	for j = 0; j < 10; j++ {
		fmt.Printf("Element[%d] = %d\n", j, n[j])
	}
}
//Element[0] = 100
//Element[1] = 101
//Element[2] = 102
//Element[3] = 103
//Element[4] = 104
//Element[5] = 105
//Element[6] = 106
//Element[7] = 107
//Element[8] = 108
//Element[9] = 109

例2:

package main

import "fmt"

func main() {
   var i, j, k int
   // 声明数组的同时进行初始化
   balance := [5]float32{1000.0, 2.0, 3.1, 7.0, 50.0}

   // 输出数组元素
   for i = 0; i < 5; i++ {
      fmt.Printf("balance[%d] = %f\n", i, balance[i])
   }

   balance2 := [...]float32{1000.0, 2.0, 3.1, 7.0, 50.0}

   // 输出每个数组元素的值
   for j = 0; j < 5; j++ {
      fmt.Printf("balance2[%d] = %f\n", j, balance2[j])
   }

   // 将索引为1,3的数组元素初始化
   balance3 := [5]float32{1:90.0, 3:77.0}
   for k = 0; k < 5; k++ {
      fmt.Printf("balance3[%d] = %f\n", k, balance3[k])
   }
}
/*
balance[0] = 1000.000000
balance[1] = 2.000000
balance[2] = 3.100000
balance[3] = 7.000000
balance[4] = 50.000000
balance2[0] = 1000.000000
balance2[1] = 2.000000
balance2[2] = 3.100000
balance2[3] = 7.000000
balance2[4] = 50.000000
balance3[0] = 0.000000
balance3[1] = 90.000000
balance3[2] = 0.000000
balance3[3] = 77.000000
balance3[4] = 0.000000
*/

多维数组

var threeim [5][10][4]int			// 声明一个三维数组

二维数组

// 二维数组
package main

import "fmt"

func main() {
   // Step1:创建数组
   values := [][]int{}

   // Step2:使用append()函数向空的二维数组添加两行一维数组
   row1 := []int{1, 2, 3}
   row2 := []int{4, 5, 6}
   values = append(values, row1)
   values = append(values, row2)

   // Step3:显示两行数据
   fmt.Println("Row1")
   fmt.Println(values[0])
   fmt.Println("Row2")
   fmt.Println(values[1])

   // Step4:访问第一个元素
   fmt.Println("第一个元素为:")
   fmt.Println(values[0][0])
}
/*
Row1
[1 2 3]
Row2
[4 5 6]
第一个元素为:
1
 */

初始化二维数组

a := [3][4]{
{0, 1, 2, 3, 4},
{5, 6, 7, 8, 9},
{10, 11, 12, 13, 14},			// 注意⚠️:这一行必须有逗号“,”因为最后一行的“}”不能单独一行
}	

// 注意⚠️
也可以这样写
a := [3][4]{
{0, 1, 2, 3, 4},
{5, 6, 7, 8, 9},
{10, 11, 12, 13, 14}}     // ⚠️
// 实例化一个2行2列的二维数组package mainimport "fmt"func main() {   // 1、创建2维数组   // sites := [2][2]string{}   sites := [][]string{}   // 2、添加元素   /*sites[0][0] = "Google"   sites[0][1] = "Runoob"   sites[1][0] = "TaoBao"   sites[1][1] = "WeiBo"*/   row1 := []string{"Hello", "World"}   row2 := []string{"Shi", "Tou"}   sites = append(sites, row1)   sites = append(sites, row2)   // 3、输出   fmt.Println(sites)    // [[Hello World] [Shi Tou]]}

访问二维数组

⚠️通过索引访问

⚠️ %v 相应值的默认格式

package main

import "fmt"

func main() {
   // 创建空的二维数组
   animals := [][]string{}

   // 创建3个长度不一的一维数组
   row1 := []string{"fish", "shark", "eel"}
   row2 := []string{"bird"}
   row3 := []string{"lizard", "salamander"}

   // 使用append()函数将1维数组添加到二维数组中
   animals = append(animals, row1)
   animals = append(animals, row2)
   animals = append(animals, row3)

   // 循环输出
   for i := range animals{
      fmt.Printf("Row: %v\n", i)     // ⚠️这里的 %v 相应值的默认格式
      fmt.Println(animals[i])
   }
}
/*
Row: 0
[fish shark eel]
Row: 1
[bird]
Row: 2
[lizard salamander]
 */

各个维度元素数量不一致的数组

package main

import "fmt"

func main() {
   // 创建空的二维数组
   animals := [][]string{}

   // 创建3个长度不一的一维数组
   row1 := []string{"fish", "shark", "eel"}
   row2 := []string{"bird"}
   row3 := []string{"lizard", "salamander"}

   // 使用append()函数将1维数组添加到二维数组中
   animals = append(animals, row1)
   animals = append(animals, row2)
   animals = append(animals, row3)

   // 循环输出
   for i := range animals{
      fmt.Printf("Row: %v\n", i)     // 这里的 %v 相应值的默认格式
      fmt.Println(animals[i])
   }
}
/*
Row: 0
[fish shark eel]
Row: 1
[bird]
Row: 2
[lizard salamander]
 */

向函数传递数组

只需要将函数形参换为数据即可

// 向函数传递数组
package main

import "fmt"

func main() {
   var balance = [5]int{1000, 2, 3, 23, 10}
   var avg float32

   // 数组作为参数传递给函数
   avg = getAverage(balance, 5)

   // 输出返回的平均值
   fmt.Printf("平均值为:%f", avg) // 平均值为:207.600006
}

func getAverage(arr [5]int, size int) float32 {
   var i, sum int
   var avg float32

   for i = 0; i < size; i++ {
      sum += arr[i]
   }

   avg = float32(sum) / float32(size)
   return avg
}
// 设置固定精度package mainimport "fmt"func main() {   a := 1690   b := 1700   c := a * b   fmt.Println(c) // 2873000   fmt.Println(float64(c))   // 2.873e+06   fmt.Println(float64(c) / 1000000)  // 2.873}

指针

变量是一种使用方便的占位符,用于引用计算机内存地址

⚠️Go中取地址符:&;例如:&a 表示取a变量相应的内存地址

package mainimport "fmt"func main() {	var a int = 10	fmt.Printf("a变量的内存地址:%x\n", &a)    // a变量的内存地址:14000122008}//⚠️ %x占位符 十六进制表示,字母形式为小写a-f

指针

⚠️指针:地址

⚠️指针变量:存储地址的变量

指针变量声明

var ip *intvar fp *float32

使用指针

  • 定义指针变量
  • 指针变量赋值
  • 访问指针变量中存储地址指向的内容
package mainimport "fmt"func main() {	var a int = 20		// 声明实际变量	var ip *int		    // 声明指针变量	ip = &a           // 指针变量存储地址	fmt.Printf("a 变量的地址是: %x\n", &a)		// a 变量的地址是: 14000124008	// 指针变量的存储地址(指针:地址)	fmt.Printf("ip 变量储存的指针(其实就是a变量的地址):%x\n", ip)	// ip 变量储存的指针地址(其实就是a变量的地址):14000124008	// 指针变量指向的内容(指针访问值)	fmt.Printf("*ip 变量的值:%d\n", *ip)	// *ip 变量的值:20}// ⚠️:指针类型变量前面加 * 号 : 获取指针指向的内容//输出结果:/**a 变量的地址是: 14000124008ip 变量储存的指针地址(其实就是a变量的地址):14000124008*ip 变量的值:20*/

Go空指针

// 空指针package mainimport "fmt"func main() {	var ptr *int	fmt.Printf("ptr 的值为:%x\n", ptr) // ptr 的值为:0	if ptr != nil {		fmt.Println("ptr不是空指针")	} else {		fmt.Println("ptr是空指针")	}	// ptr是空指针}

指针数组

需要保存数组,就需要用到指针

package main

import "fmt"

const MAX int = 3

func main() {
   a := []int{10, 100, 1000}
   var i int
   var ptr [MAX]*int

   for i = 0; i < MAX; i++ {
      ptr[i] = &a[i] /* 整数地址赋值给指针数组 */
   }

   for i = 0; i < MAX; i++ {
      fmt.Printf("a[%d] = %d\n", i, *ptr[i])
   }
   
}

//a[0] = 10
//a[1] = 100
//a[2] = 1000

指向指针的指针

一个指针变量存放的是另一个指针变量的地址。

// 声明指向指针的指针变量
var ptr **int

访问指向指针的指针变量值——使用 ** 号

// 访问指向指针的指针变量值——**
package main

import "fmt"

func main() {
   var a int
   var ptr *int
   var pptr **int

   a = 3000

   // 指针ptr的地址
   ptr = &a

   // 指向指针 ptr 的地址
   pptr = &ptr

   // 获取 pptr 的值
   fmt.Printf("变量a = %d\n", a)       // 3000
   fmt.Printf("指针变量 *ptr = %d\n", *ptr)   // 3000
   fmt.Printf("指向指针的指针变量 **pptr = %d\n", **pptr)  // 3000
}

指针作为函数参数

将函数参数设置为指针类型即可

package main

import "fmt"

func main() {
   // 定义局部变量
   var a int = 100
   var b int = 200

   fmt.Printf("交换前 a 的值: %d\n", a)    // 100
   fmt.Printf("交换前 b 的值: %d\n", b)    // 200

   /*
   * &a指向 a 变量的地址
   * &b指向 b 变量的地址
    */
   swap(&a, &b)

   fmt.Printf("交换后 a 的值: %d\n", a)    // 200
   fmt.Printf("交换后 b 的值: %d\n", b)    // 100
}

func swap(x *int, y *int) {
   var temp int
   temp = *x // 保存 x 地址的值
   *x = *y   // 将y赋值给x
   *y = temp
}

结构体

定义结构体

结构体类型变量使用struct定义

package main

import "fmt"

type Books struct {
   title   string
   author  string
   subject string
   book_id int
}

func main() {

   // 创建一个新的结构体
   fmt.Println(Books{"Go 语言", "dashazi", "Go 语言笔记", 6523432})

   // 使用key => value 格式
   fmt.Println(Books{title: "Go 语言", author: "dashazi", subject: "Go 语言笔记", book_id: 6523432})

   // 忽略的字段为 0 或 空
   fmt.Println(Books{title: "Go 语言", subject: "Go 语言笔记"})
}
/*
{Go 语言 dashazi Go 语言笔记 6523432}
{Go 语言 dashazi Go 语言笔记 6523432}
{Go 语言  Go 语言笔记 0}
*/

访问结构体成员——“.”

// 访问结构体成员——"."
package main

import "fmt"

type Books struct {
   title   string
   author  string
   subject string
   book_id int
}

func main() {

   var book1 Books // 声明book1的类型为Books
   var book2 Books // 声明book2的类型为Books

   // book1描述
   book1.title = "Go 语言"
   book1.author = "dashazi"
   book1.subject = "Go 语言教程"
   book1.book_id = 31252435

   // book2描述
   book2.title = "Python 语言"
   book2.author = "dashazi 1号"
   book2.subject = "Python 语言教程"
   book2.book_id = 31252434

   // 打印book1信息
   fmt.Printf("book1.title = %s\n", book1.title)
   fmt.Printf("book1.author = %s\n", book1.author)
   fmt.Printf("book1.subject = %s\n", book1.subject)
   fmt.Printf("book1.book_id = %d\n", book1.book_id)

   // 打印book2信息
   fmt.Printf("book2.title = %s\n", book2.title)
   fmt.Printf("book2.author = %s\n", book2.author)
   fmt.Printf("book2.subject = %s\n", book2.subject)
   fmt.Printf("book2.book_id = %d\n", book2.book_id)

}
/*
book1.title = Go 语言
book1.author = dashazi
book1.subject = Go 语言教程
book1.book_id = 31252435
book2.title = Python 语言
book2.author = dashazi 1号
book2.subject = Python 语言教程
book2.book_id = 31252434
*/

结构体作为函数参数

// 结构体作为函数参数
package main

import "fmt"

type Books struct {
   title string
   author string
   subject string
   book_id int
}

func main() {
   var book1 Books // 声明book1的类型为Books
   var book2 Books // 声明book2的类型为Books

   // book1描述
   book1.title = "Go 语言"
   book1.author = "dashazi"
   book1.subject = "Go 语言教程"
   book1.book_id = 31252435

   // book2描述
   book2.title = "Python 语言"
   book2.author = "dashazi 1号"
   book2.subject = "Python 语言教程"
   book2.book_id = 31252434

   // 打印 book1 信息
   printBook(book1)

   // 打印 book2 信息
   printBook(book2)
}

func printBook(book Books) {
   fmt.Printf("book.title = %s\n", book.title)
   fmt.Printf("book.author = %s\n", book.author)
   fmt.Printf("book.subject = %s\n", book.subject)
   fmt.Printf("book.book_id = %d\n", book.book_id)
}
/*
book.title = Go 语言
book.author = dashazi
book.subject = Go 语言教程
book.book_id = 31252435
book.title = Python 语言
book.author = dashazi 1号
book.subject = Python 语言教程
book.book_id = 31252434
 */

结构体指针

定义指向结构体的指针

var struct_pointer *Books			// 定义的这个指针变量可以存储结构体变量的地址
struct_pointer = &Book1				// 查看结构体变量的地址
// 结构体指针
package main

import "fmt"

type Books struct {
   title   string
   author  string
   subject string
   book_id int
}

func main() {
   var book1 Books // 声明book1的类型为Books
   var book2 Books // 声明book2的类型为Books

   // book1描述
   book1.title = "Go 语言"
   book1.author = "dashazi"
   book1.subject = "Go 语言教程"
   book1.book_id = 31252435

   // book2描述
   book2.title = "Python 语言"
   book2.author = "dashazi 1号"
   book2.subject = "Python 语言教程"
   book2.book_id = 31252434

   // 打印 book1 信息
   printBook(&book1)

   // 打印 book2 信息
   printBook(&book2)
}

func printBook(book *Books) {
   fmt.Printf("book.title = %s\n", book.title)
   fmt.Printf("book.author = %s\n", book.author)
   fmt.Printf("book.subject = %s\n", book.subject)
   fmt.Printf("book.book_id = %d\n", book.book_id)
}
/*
book.title = Go 语言
book.author = dashazi
book.subject = Go 语言教程
book.book_id = 31252435
book.title = Python 语言
book.author = dashazi 1号
book.subject = Python 语言教程
book.book_id = 31252434
 */

切片

切片是针对数组的长度不变提出的——“动态数组”

定义切片

可以使用一个未指定大小的数组来定义切片或者使用make()函数来创建切片

var identifier []type

// 使用make()函数创建切片
var slice1 []type = make([]type, len)
或
slice1 := make([]type, len)

// 指定容量
make([]T, length, capacity)

// ⚠️len是数组长度,也是切片的初始长度

初始化切片

1、直接初始化切片

s := [] int {1, 2, 3}
// []表示是切片类型

2、初始化切片s为数组arr的引用

s := arr[:]s := arr[startIndex : endIndex]s := arr[startIndex : ]s := arr[: endIndex]

3、通过切片s初始化切片s1

s1 := s[startIndex : endIndex]

4、使用make()函数初始化

s := make([]int, len, cap)// []int 标识 表示元素类型为int的切片

len()、cap()函数

package mainimport "fmt"func main() {	var numbers = make([]int, 3, 5)		// 初始化一个长度为3,容量为5的整型切片	printSlice(numbers)		// len = 3 cap = 5 slice = [0 0 0]}func printSlice(x []int){	fmt.Printf("len = %d cap = %d slice = %v\n", len(x), cap(x), x)}

空(nil)切片——未初始化之前的切片,默认为nil,长度为0

package mainimport "fmt"func main() {	var numbers []int			printSlice(numbers)		// len = 0 cap = 0 slice = []}func printSlice(x []int){	fmt.Printf("len = %d cap = %d slice = %v\n", len(x), cap(x), x)}

切片截取

设置上下限[lower-bound : upper-bound]

append()、copy()函数

增加切片容量,创建一个更大的切片把原来的切片内容拷贝过来

package main

import "fmt"

func main() {
   var numbers []int
   printSlice(numbers)   // len = 0 cap = 0 slices = []

   // 允许追加空切片
   numbers = append(numbers, 0)
   printSlice(numbers)       // len = 1 cap = 1 slices = [0]

   // 向切片添加一个元素
   numbers = append(numbers, 1)
   printSlice(numbers)       // len = 2 cap = 2 slices = [0 1]

   // 同时添加多个元素
   numbers = append(numbers, 2, 3, 4)
   printSlice(numbers)       // len = 5 cap = 6 slices = [0 1 2 3 4]

   // 创建切片numbers1是numbers切片的2倍容量
   numbers1 := make([]int, len(numbers), (cap(numbers)) * 2)

   // 拷贝numbers中的内容到numbers1中
   copy(numbers1, numbers)
   printSlice(numbers1)   // len = 5 cap = 12 slices = [0 1 2 3 4]
}

func printSlice(x []int) {
   fmt.Printf("len = %d cap = %d slices = %v\n", len(x), cap(x), x)
}

range关键字

range关键字用于for循环中迭代数组(array)、切片(slice)、通道(channel)、集合(map)元素

⚠️数组和切片中返回元素的索引和索引对应的值,集合中返回key-value对

// 不想创建一个变量用"_"「空白符」,想创建一个变量用字母
package main

import "fmt"

func main() {
	// 使用range求slice的和/和数组类似
	nums := []int{1, 2, 3, 4}
	sum := 0
	for _, num := range nums {
		sum += num
	}
	fmt.Println("sum:", sum) // sum: 10
	// ⚠️在数组上使用range会传入index和值两个变量。上例中我们不使用索引,因此使用空白符"_"省略了索引号。

	// 需要知道索引号时
	for i, num := range nums {
		if num == 3 {
			fmt.Println("index", i) // index 2
		}
	}

	// range用在map键值对中
	kvs := map[string]string{"a": "apple", "b": "banana"}
	for k, v := range kvs {
		fmt.Printf("%s ---> %s\n", k, v)
		//a ---> apple
		//b ---> banana
	}

	// range也可以用在枚举Unicode字符串。第一个参数:字符的索引,第二个参数:字符(Unicode的值)本身
	for i, c := range "go" {
		fmt.Println(i, c)
		//0 103
		//1 111
	}
}

Map(集合)

Map是一种无序键值对集合。‼️可以通过key快速检索数据,key类似索引指向数据的值。

Map是无序的,因此无法决定它的返回顺序(因为Map是使用hash表实现的)

定义Map

// 声明变量,默认map为nilvar map_variable map[key_data_type]value_data_type// 使用make函数定义mapmap_variable := make(map[key_data_type]value_data_type)

⚠️不初始化map,会创建一个nil map。nil map不能用来存储键值对

package main

import "fmt"

func main() {
   var coutryCapitalMap map[string]string     // 创建集合
   coutryCapitalMap = make(map[string]string) // ⚠️这里是"="不是":="

   // map插入键值对
   coutryCapitalMap["France"] = "巴黎"
   coutryCapitalMap["Italy"] = "罗马"
   coutryCapitalMap["japan"] = "东京"
   coutryCapitalMap["india"] = "新德里"

   // 使用键输出map值
   for coutry := range coutryCapitalMap {
      fmt.Println(coutry, "首都是:", coutryCapitalMap[coutry])
   }
   //France 首都是: 巴黎
   //Italy 首都是: 罗马
   //japan 首都是: 东京
   //india 首都是: 新德里

   // 查看元素在集合中是否存在
   capital, ok := coutryCapitalMap["American"]       // ⚠️如果确定是真实的,则存在,否则不存在
   fmt.Println(capital)
   fmt.Println(ok)          // ⚠️false
   if ok {
      fmt.Println("American 的首都是:", capital)
   } else {
      fmt.Println("American 的首都不存在")    // American的首都不存在
   }
}

delete()函数

delete()函数用于删除集合元素,参数为map和对应的key

package main

import "fmt"

func main() {
   coutryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}

   fmt.Println("原始Map")

   // 打印地图
   for coutry := range coutryCapitalMap {
      fmt.Println(coutry, "首都是", coutryCapitalMap[coutry])
   }

   // 删除元素
   delete(coutryCapitalMap, "France")
   fmt.Println("法国条目被删除")

   fmt.Println("删除后的条目")

   // 打印地图
   for coutry := range coutryCapitalMap {
      fmt.Println(coutry, "首都是:", coutryCapitalMap[coutry])
   }
}
/*
原始Map
Japan 首都是 Tokyo
India 首都是 New delhi
France 首都是 Paris
Italy 首都是 Rome
法国条目被删除
删除后的条目
Italy 首都是: Rome
Japan 首都是: Tokyo
India 首都是: New delhi
 */

递归

在运行过程中调用自己

// 递归函数
package main

func recursion() {
   recursion()		// 调用自己
}
func main() {
   recursion()
}

阶乘

// 阶乘
package main

import "fmt"

func Factorial(n uint64) (result uint64) {
   if n > 0 {
      result = n * Factorial(n-1)
      return result
   }
   return 1
}
func main() {
   var i int = 15
   fmt.Printf("%d 的阶乘是 %d\n", i, Factorial(uint64(i)))   // 15 的阶乘是 1307674368000
}

斐波拉契数列

// 斐波拉契数列
package main

import "fmt"

func main() {
   var i int
   for i = 0; i < 10; i++ {
      fmt.Printf("%d\t", fibonacci(i))
   }
}

func fibonacci(n int) int {
   if n < 2 {
      return n
   }
   return fibonacci(n-2) + fibonacci(n-1)
}

//0       1       1       2       3       5       8       13      21      34

类型转换

type_name(expression)
package main

import "fmt"

func main() {
   var sum int = 17
   var count int = 5
   var mean float32

   mean = float32(sum) / float32(count)
   fmt.Printf("mean 的值为:%f\n", mean)     // mean 的值为:3.400000
  
}

接口

⚠️接口是:go语言的一种数据类型

把所用具有共性的方法定义在一起。任何其他类型只要实现了这些方法就是实现了这个接口

package main

import "fmt"

type Phone interface {
   call()
}

type NokiaPhone struct {
}

func (nokiaPhone NokiaPhone) call() {
   fmt.Println("I am Nokia, I can call you")
}

type IPhone struct {
}

func (iPhone IPhone) call() {
   fmt.Println("I am iPhone, I can call you")
}

func main() {
   var phone Phone

   phone = new(NokiaPhone)       // new()函数分配内存
   phone.call()

   phone = new(IPhone)
   phone.call()
}
//I am Nokia, I can call you
//I am iPhone, I can call you

Go并发

拓展(串行、并发、并行)

通过关键字go开启goroutine,goroutine是轻量级线程,

语法格式

go 函数名(参数列表)

例如:
go f(x, y, z)		// 开启一个新的goroutine

Go允许使用go语句开启一个新的运行期线程,即goroutine,以一个不同的、新创建的goroutine来执行一个函数。同一个程序的所有goroutine共享同一个地址空间。

// Go语言并发
package main

import (
   "fmt"
   "time"
)

func say(s string) {
   for i := 0; i < 5; i++ {
      time.Sleep(100 * time.Millisecond)
      fmt.Println(s)
   }
}
func main() {
   go say("world")
   say("hello")
}
/*
world
hello
hello
world
world
hello
hello
world
world
hello
 */

⚠️代码中输出的hello和world没有固定先后顺序,因为是两个goroutine在执行

通道(channel)

⚠️是用来传递数据的一种数据结构

通道可用来在两个goroutine之间通过传递一个指定类型的值来同步运行和通讯。

操作符“<-”用于指定通道的方向,发送或者接收。未指定方向则为双通道。

ch <- v		// 把v发送到通道ch
v := <-ch	// 从通道ch接收数据;并将数据赋值给v

声明通道(使用前先创建)

ch := make(chan int)

注意⚠️:默认情况下,通道是不带缓冲区的。发送端发送数据,必须有相应的接收端接收数据。

//通道(channel)
package main

import "fmt"

func sum(s []int, c chan int) {
   sum := 0
   for _, v := range s {
      sum += v
   }
   c <- sum // 将sum发送到通道c
}
func main() {
   s := []int{7, 4, 8, -9, 0, 2}

   c := make(chan int)       // 声明通道

   go sum(s[len(s)/2:], c)       // 一个goroutine
   go sum(s[:len(s)/2], c)       // 一个goroutine

   x, y := <-c, <-c         // 从通道c中接收数据

   fmt.Println(x, y, x+y) // 19 -7 12

}

通道缓冲区

通道的缓冲区,通过设定make的第二个参数指定缓冲区大小:

ch := make(chan int, 100)

带缓冲区的通道允许发送端发送数据和接收端接收数据处于异步状态(发送端发送的数据可以先放在缓冲区中,等待接收端获取数据,而不是立刻需要接收端区获取数据)

⚠️:如果不带缓冲区,发送方会阻塞直到接收方从通道中接收了值。带缓冲区,发送方会阻塞直到发送的值被拷贝到缓冲区。

// 缓冲区
package main

import "fmt"

func main() {
   // 这里定义了一个可以存储整数类型的带缓冲通道
   // 缓冲区大小为2
   ch := make(chan int, 2)


   // 因为ch是带缓冲通道的,因此可以同时发送两个数据,而不需要立刻区同步读取数据
   ch <- 1
   ch <- 2

   // 获取数据
   fmt.Println(<-ch)  // 1
   fmt.Println(<-ch)  // 2
}
// ⚠️以下程序报错
package main

import "fmt"

func main() {
   ch := make(chan int)

   ch <- 1		// 发送数据给通道ch,注意⚠️这里没有接收方接收,发送的数据

   // 获取数据
   fmt.Println(<-ch) 
  
}

// 报错❌:fatal error: all goroutines are asleep - deadlock!

Go遍历通道和关闭通道

package main

import "fmt"

func fibonacci(n int, c chan int) {
   x, y := 0, 1
   for i := 0; i < n; i++ {
      c <- x
      x, y = y, x+y
   }
   close(c)		// 关闭通道c
}

func main() {
   c := make(chan int, 10)
   go fibonacci(cap(c), c)
   /*
   range函数遍历每个从通道接收的数据,因为c在发送完10个数据之后就关闭了通道,所以这里我们的range函数在接收
   到10个数据之后就结束了。如果上面的c通道不关闭,range函数就不会关闭,从而接收第11个数据的时候就阻塞了。
    */
   for i := range c {    
      fmt.Println(i)
   }
}
/*
0
1
1
2
3
5
8
13
21
34
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值