Go语言基础归纳(Golang学习笔记)

Go基础

(一)程序举例

hello.go

package main

import "fmt"

func main() {
    fmt.Println("Hello World")
}

运行:

$ go run hello.go

$ go build hello.go
$ ./hello

注意事项:

  • 源文件开头的第一行必须指明该文件属于哪个包,每个Go程序都含有一个名为main的包
  • 程序种使用的所有包必须先引入,引入的包必须使用
  • 每个Go程序必须含有一个man函数,该函数一般是启动后第一个执行的函数(除非有init函数)
  • 当标识符以大写字母开头,则它能被外部引用,称导出,否则不能被外部引用
  • "{"不能单独占一行

(二)基础语法

1. 行分隔符

Go程序的一行代表一个语句,若要在一行内写多个语句,则要用";"分隔

2. 注释

//	单行注释
/*
	多行注释
*/

3. 标识符

由数字、字母、下划线组成,数字不能放开头

(三)数据类型

在这里插入图片描述

(四)变量

1. 变量声明

Go程序中,变量声明后必须使用

(1)var关键字
package main

import "fmt"

func main() {
    
    var a string = "hello"	// 单个变量声明并初始化
    fmt.Println(a)			// hello
    
    var b, c int = 1, 2		// 多个变量声明并初始化
    fmt.Println(b, c)		// 1, 2
    
    var d bool				// 单个变量声明未初始化,使用零值
    fmt.Println(d)			// false
    
}

数字型、布尔型、字符串的零值为0、flase、“”,其他一般为nil

(2)根据值自动推断类型

var a = true,a自动推断为布尔型,值为true

(3)简略写法

:=只能在函数体内出现,a := 1等价于:

var a int
a = 1

:=也可以用来同时声明并初始化多个变量,如:a, b, c = 1, 2, "hello",等价于:

var a, b int
var c string
a, b, c = 1, 2, "hello"
(4)交换值

交换值可以通过a, b = b, a实现,前提是a、b的类型相同

(5)抛弃值

_, b = 1, 2中,值1被抛弃,只有b被赋值为2

2. 变量作用域

(1)局部变量

在函数体内声明的变量,作用域在函数体内

(2)全局变量

在函数体外声明的变量,可在整个包或包外部(被导出后)使用

全局变量与局部变量同名,优先考虑局部变量

(3)形式参数

在函数定义中的变量,作为局部变量使用

(五)常量

1. 变量声明

常量只能是布尔型、数字型、字符串型,用const关键字声明

显式声明:const a string = "hello"

隐式声明:const a = "hello"

可同时声明并初始化多个常量

可用作枚举:

const (
	Unknow = 0
    Female = 1
    Male = 2
)

2. 特殊常量iota

iota是一个可以被编译器修改的常量,第一个iota等于0,const中每增加一行,它的值增加1

const (
	a = iota	// 0
    b			// 1
    c = "hello"	// "hello"
    d 			// "hello"
    e = iota    // 4
    f 			// 5
)

(六)运算符

在这里插入图片描述

(七)条件语句

1. if 语句

if 条件 {
    语句
}

2. if…else 语句

if 条件 {
    语句
} else {
    语句
}

3. if嵌套语句

if 条件1 {
    语句1
    if 条件2 {
        语句2
    }
}

4. switch语句

switch 变量 {
    case1:
    	语句1
    case2:
    	语句2
    default:
    	语句n
}

5. select语句

类似于用于通信的switch语句,每个case必须是一个通信操作

package main

import (
	"fmt"
)

func main() {

	ch := make(chan int, 10)
	ch <- 1
	ch <- 2
	ch <- 3
	close(ch)

	for {
		select {
		case v, ok := <-ch:
			if ok {
				fmt.Printf("v=%v, ok=%v \n", v, ok)
			} else {
				fmt.Println("channel is closed")
				return
			}
		default:
			fmt.Println("")
		}
	}
}

(八)循环语句

1. for语句

for 变量赋初值; 循环控制条件; 变量增或减 {
    语句
} 
for 循环控制条件 {
    语句
} 
for {
    语句
} 

2. 嵌套循环

for 变量赋初值; 循环控制条件; 变量增或减 {
    语句
    for 变量赋初值; 循环控制条件; 变量增或减 {
    语句
	} 
}  

3. 循环控制语句

描述
break语句跳出当前循环或 switch 语句
continue语句跳过当前循环的剩余语句进入下一轮循
goto语句将控制转移到标记语句
goto 标记
...
标记: 语句

(九)函数

1. 函数定义

格式:

func 函数名(参数列表) 返回类型 {
    函数体
}

举例:

/* 返回一个值:函数返回两个数的最大值 */
func max(num1, num2 int) int {
    
    var result int
    
    if (num1 > num2) {
        result = num1
    } else {
        result = num2
    }
    
    return result
}
/* 返回多个值:函数交换两个数的值并返回 */
func swap(num1, num2) (int, int) {
    return num2, num1
}

2. 函数调用

举例:

package main

import "fmt"

func main() {

	var a int = 1
	var b int = 2
	var ret int
	/* 调用函数并返回最大值 */
	ret = max(a, b)
	fmt.Printf("max = %d\n", ret)

}

/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {

	var result int

	if num1 > num2 {
		result = num1
	} else {
		result = num2
	}

	return result
}

举例:

package main

import "fmt"

func main() {

	var a int = 1
	var b int = 2
    fmt.Printf("before: a = %d, b = %d\n", a, b)
    /* 调用函数并返回交换的两个值 */
	a, b = swap(a, b)
    fmt.Printf("after:  a = %d, b = %d\n", a, b)

}

/* 函数交换两个数的值并返回 */
func swap(num1, num2 int) (int, int) {
	return num2, num1
}

函数调用传递参数的方式:

描述
值传递调用函数时将实参复制一份传递到函数中,在函数中对参数进行的修改不会影响到实参
引用传递调用函数时将实参的地址传递到函数中,在函数中对参数进行的修改会影响到实参
  • 默认情况下Go使用值传递

值传递举例:

package main

import "fmt"

func main() {
    
    var a int = 1
    var b int = 2
    
    fmt.Printf("before: a = %d, b = %d\n", a, b)	// a = 1, b = 2    
    /* 调用函数交换值 */
    swap(a, b)
    fmt.Printf("after:  a = %d, b = %d\n", a, b)	// a = 1, b = 2  
    
}
/* 定义交换值的函数 */
func swap(x, y int) {
    
    var temp int
    
    temp = x	// 保存x上的值
    x = y		// 将y的值赋给x
    y = temp	// 将temp的值赋给y
    
}

引用值传递:

package main

import "fmt"

func main() {
    
    var a int = 1
    var b int = 2
    
    fmt.Printf("before: a = %d, b = %d\n", a, b)	// a = 1, b = 2    
    /* 调用函数交换值 */
    swap(&a, &b)	
    fmt.Printf("after:  a = %d, b = %d\n", a, b)	// a = 2, b = 1
    
}
/* 定义交换值的函数 */
func swap(x, y *int) {
    
    var temp int
    
    temp = *x	// 保存x地址上的值
    *x = *y		// 将y的值赋给x
    *y = temp	// 将temp的值赋给y
    
}

3. 函数用法

Go语言中,函数可以作为另一个函数的实参

package main

import (
	"fmt"
	"math"
)

func main() {

	/* 声明函数变量 */
	getSquareRoot := func(x float64) float64 {
		return math.Sqrt(x)
	}

	/* 使用函数 */
	fmt.Println(getSquareRoot(9))	// 开方

}

Go语言支持匿名函数,可作为闭包,匿名函数可直接使用函数内的变量

package main

import "fmt"

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
}

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

Go语言中的方法为包含接收者的函数

格式:

func (参数列表) 方法名() 返回类型 {
    函数体
}

举例:

package main

import "fmt"

// 定义结构体
type Circle struct {
	radius float64
}

func main() {

	var c1 Circle
	c1.radius = 10.00
	fmt.Println("area = ", c1.getArea())	// 314

}

// Circle类型对象中的方法
func (c Circle) getArea() float64 {
	return 3.14 * c.radius * c.radius
}

(十)数组

  • 数组:类型相同的已编号且长度固定的序列

1. 数组声明

格式:

var 数组名 [长度] 类型

举例:

var array [5] int

2. 数组初始化

var array = [5]float32{0.0, 2.0, 4.0, 6.0, 8.0} 
array := [5]float32{0.0, 2.0, 4.0, 6.0, 8.0}

数组长度不定,元素确定:

var array = [...]float32{0.0, 2.0, 4.0, 6.0, 8.0}
array := [...]float32{0.0, 2.0, 4.0, 6.0, 8.0}

数组长度确定,元素不确定

array := [5]float32{1:2.0, 3:6.0}	// 索引为1、3的元素初始化为2.0、6.0
array[4] = 8.0	// 索引为4的元素初始化为8.0

3. 数组访问

直接通过索引读取

package main

import "fmt"

func main() {

	var a [10]int
	
	/* 为数组a初始化元素 */
	for i := 0; i < 10; i++ {
		a[i] = i + 100
	}

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

结果为:

a[0] = 100
a[1] = 101
a[2] = 102
a[3] = 103
a[4] = 104
a[5] = 105
a[6] = 106
a[7] = 107
a[8] = 108
a[9] = 109

4. 多维数组

格式:

var 数组名 [一维长度][二维长度]...[n维长度] 数组类型

二维数组定义:

var array [3][4] 

二维数组初始化:

array := [3][4]int{
    {0, 1, 2, 3},
    {4, 5, 6, 7},
    {0, 1, 2, 3},
    {4, 5, 6, 7},
}
array := [3][4]int{
    {0, 1, 2, 3},
    {4, 5, 6, 7},
    {0, 1, 2, 3},
    {4, 5, 6, 7}}

二维数组访问:

var val int = array[2][3]
val := array[2][3]

举例:

package main

import "fmt"

func main() {

	// 创建空的二维数组
	a := [][]int{}

	// 创建3个一维数组
	r1 := []int{1}
	r2 := []int{2, 3}
	r3 := []int{4, 5, 6}

	// 将一维数组添加到二维数组
	a = append(a, r1)
	a = append(a, r2)
	a = append(a, r3)

	// 循环输出
	for i := range a {
		fmt.Println(a[i])
	}

}

输出:

[1]
[2 3]  
[4 5 6]

5. 数组作函数参数

package main

import "fmt"

func main() {

	var score = [5]float32{95.0, 80.0, 90.0, 85.0, 98.0}
	var avg float32

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

	fmt.Println(avg) // 89.6

}

func getAverage(a [5]float32, size int) float32 {

	var sum float32

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

	return sum / float32(size)

}

(十一)指针

指针变量指向一个值的内存地址

1. 指针使用

package main

import "fmt"

func main() {

    a := 20			// 声明实际变量
	var p *int		// 声明指针变量

	p = &a			// 指针变量赋值

	fmt.Printf("a变量的值是:%d\n", a)			
	fmt.Printf("a变量的存储地址是:%d\n", &a)		
	fmt.Printf("p变量存储的地址是:%d\n", p)		// 访问指针变量
	fmt.Printf("p变量所指地址的值是:%d\n", *p)		// 访问指针变量指向的值

}

2. 指针数组

package main

import "fmt"

const MAX int = 3

func main() {

	a := []int{1, 2, 3}
	var p [MAX]*int		// 声明指针数组

	for i := 0; i < MAX; i++ {
		p[i] = &a[i]	// 给指针数组的每个元素赋值
	}

	for i := 0; i < MAX; i++ {
		fmt.Printf("a[%d] = %d\n", i, *p[i])	// 访问指针数组每个指针指向的值
	}

}
a[0] = 1
a[1] = 2
a[2] = 3

3. 指向指针的指针

package main

import "fmt"

func main() {

    a := 20			// 声明实际变量
	var p *int		// 声明指针变量
	var pp **int	// 声明指向指针的变量

	p = &a			// 指针变量赋值
	pp = &p			// 指向指针的指针变量赋值

	fmt.Printf("变量a = %d\n", a)
	fmt.Printf("指针变量*p = %d\n", *p)
	fmt.Printf("指向指针的指针变量**pp = %d\n", **pp)

}

4. 指针作函数参数

package main

import "fmt"

func main() {

	var a int = 100
	var b int = 200

	fmt.Printf("Before change: a = %d, b = %d\n", a, b) // Before change: a = 100, b = 200

	swap(&a, &b) // 指针作函数参数

	fmt.Printf("After change: a = %d, b = %d\n", a, b) // After change: a = 200, b = 100

}

func swap(x *int, y *int) {

	var temp int

	temp = *x
	*x = *y
	*y = temp
}

(十二)结构体

1. 定义结构体

格式:

type 结构体名 struct {
    成员1 成员类型1
    成员2 成员类型2
}
结构体 := 结构体名{成员值1, 成员值2}
结构体 := 结构体名{成员1: 成员值1, 成员2:成员值2}

举例:

package main

import "fmt"

type Student struct {
	name string
	sex string
	age int
}

func main() {

	// 创建结构体
	student1 := Student{"Tom", "male", 20}
	student2 := Student{name: "Amy", sex : "female", age: 18}

	fmt.Println(student1)
	fmt.Println(student2)

}
{Tom male 20}
{Amy female 18}

2. 访问结构体成员

格式:

结构体.成员名

举例:

package main

import "fmt"

type Student struct {
	name string
	sex string
	age int
}

func main() {

	// 声明结构体
	var student1 Student

	// 结构体描述
	student1.name = "Tom"
	student1.sex = "male"
	student1.age = 20

	fmt.Printf("name: %s\n", student1.name)
	fmt.Printf("sex: %s\n", student1.sex)
	fmt.Printf("age: %d\n", student1.age)

}
name: Tom
sex: male
age: 20

3. 结构体作函数参数

package main

import "fmt"

type Student struct {
	name string
	sex string
	age int
}

func main() {

	// 声明结构体
	var student1 Student

	// 结构体描述
	student1.name = "Tom"
	student1.sex = "male"
	student1.age = 20

	printInfo(student1)	// 结构体作函数参数

}

func printInfo(student Student) {

	fmt.Printf("name: %s\n", student.name)
	fmt.Printf("sex: %s\n", student.sex)
	fmt.Printf("age: %d\n", student.age)

}
name: Tom
sex: male
age: 20  

4. 结构体指针

package main

import "fmt"

type Student struct {
	name string
	sex string
	age int
}

func main() {

	var student1 Student	// 声明结构体
	var p *Student			// 声明结构体指针

	// 结构体描述
	student1.name = "Tom"
	student1.sex = "male"
	student1.age = 20

	p = &student1	// 结构体指针赋值

	printInfo(p)	// 结构体指针作函数参数

}

func printInfo(p *Student) {

	fmt.Printf("name: %s\n", p.name)
	fmt.Printf("sex: %s\n", p.sex)
	fmt.Printf("age: %d\n", p.age)

}
name: Tom
sex: male
age: 20

(十三)切片

切片是对数组的抽象,与数组相比切片的长度不固定,可追加元素

1. 定义切片

用未指定大小的数组定义切片:

var 切片名 []切片类型

make()函数创建切片(容量可省略):

var 切片名 []切片类型 = make([]切片类型, 长度, 容量)
切片名 := make([]切片类型, 长度, 容量)

2. 初始化切片

直接初始化:

s := []int{1, 2, 3, 4, 5}

[]表示切片类型,切片元素为1, 2, 3, 4, 5,其长度和容量均为5

举例:

package main

import "fmt"

func main() {

	s := []int{1, 2, 3, 4, 5}

	fmt.Println(s1)	// [1 2 3 4 5]

}

初始化数组的引用:

s := a[:]					// 将数组a的所有元素创建为一个切片
s := a[stratIndex:endIndex]	// 将数组a中下标为startIndex到endIndex-1的元素创建为一个切片
s := a[stratIndex:]	// 将数组a从下标为startIndex后的元素始创建为一个切片
s := a[:endIndex]	// 将数组a从下标为endIndex-1前的元素创建为一个切片

举例:

package main

import "fmt"

func main() {

	a := [5]int{0, 2, 4, 6, 8}

	s1 := a[:]
	fmt.Println(s1)	// [0 2 4 6 8]

	s2 := a[1:4]
	fmt.Println(s2)	// [2 4 6]

	s3 := a[1:]
	fmt.Println(s3)	// [2 4 6 8] 

	s4 := a[:4]
	fmt.Println(s4)	// [0 2 4 6]

}

3. 截取切片

package main

import "fmt"

func main() {

	// 创建切片
	s := []int{0, 1, 2, 3, 4}

	// 打印原始切片
	fmt.Println("s = ", s)				// s =  [0 1 2 3 4]
	// 打印从1(包含)到4(不含)
	fmt.Println("s[1:4] = ", s[1:4])	// s[1:4] =  [1 2 3]
	// 默认下限为0
	fmt.Println("s[:4] = ", s[:4])		// s[:4] =  [0 1 2 3]
	// 默认上限为len(s)
	fmt.Println("s[1:] = ", s[1:])		// s[1:] =  [1 2 3 4]

}

4. len()和cap()函数

  • len()函数可获得切片长度
  • cap()函数可获得切片的最大长度
package main

import "fmt"

func main() {

	var s []int				// 切片未初始化,为nil
	printSlice(s)			// len = 0, cap = 0, slice = []
	s = make([]int, 3, 5)	// 切片初始化
	printSlice(s)			// 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)

}

5. append()和copy()函数

  • append()追加元素
  • copy()拷贝切片
package main

import "fmt"

func main() {

	var s1 []int		
	printSlice(s1)			// len = 0, cap = 0, slice = []

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

	// 向切片添加多个元素
	s1 = append(s1, 1, 2, 3)
	printSlice(s1)			// len = 4, cap = 4, slice = [0 1 2 3]

	// 创建切片s2是之前切片的两倍容量
	s2 := make([]int, len(s1), cap(s1) * 2)

	// 拷贝切片s1的内容到s2
	copy(s2, s1)
	printSlice(s2)			// len = 4, cap = 8, slice = [0 1 2 3]


}

func printSlice(x []int) {

	fmt.Printf("len = %d, cap = %d, slice = %v\n", len(x), cap(x), x)

}

(十四)范围

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

格式:

for key, value := range oldMap {
	...
}
for key := range oldMap {
    ...
}
for _, value := range oldMap {
    ...
}

举例:

package main

import "fmt"

func main() {

	var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

	for i, v := range pow {
		fmt.Printf("2**%d = %d\n", i, v)
	}

}
2**0 = 1
2**1 = 2
2**2 = 4
2**3 = 8
2**4 = 16
2**5 = 32
2**6 = 64
2**7 = 128

(十五)集合Map

Map是一种集合,可迭代,但由于Map是无序的,返回顺序不确定

定义:

var 集合名[键类型]值类型

初始化(不初始化map为nil):

集合名 := make(map[键类型][值类型])

插入元素值

集合名 [key] = value

删除元素值

delete(集合名, key)

举例:

package main

import "fmt"

func main() {

	countryCaptialMap := make(map[string]string)	// 创建并初始化Map
	
	// map插入键值对
	countryCaptialMap ["China"] = "北京"
	countryCaptialMap ["Janpan"] = "东京"
	countryCaptialMap ["Italy"] = "罗马"

	// 输出键值对
	fmt.Println("原始地图")
	for country := range countryCaptialMap {
		fmt.Println(country, "首都是", countryCaptialMap[country])
	}

	// 删除元素
	delete(countryCaptialMap, "Janpan")

	// 输出键值对
	fmt.Println("删除后的地图")
	for country := range countryCaptialMap {
		fmt.Println(country, "首都是", countryCaptialMap[country])
	}

}
原始地图
China 首都是 北京
Janpan 首都是 东京
Italy 首都是 罗马
删除后的地图
China 首都是 北京

(十六)递归函数

递归函数即在运行过程中调用自己

举例:递归函数实现斐波那契数列

package main

import "fmt"

func fib(n int) int {

	if n < 2 {
		return n
	}
	return fib(n - 2) + fib(n -1)

}

func main() {

	for i := 1; i < 10; i++ {
		fmt.Printf("%d\t", fib(i))
	}

}
1       1       2       3       5       8       13      21      34

(十七)类型转换

格式:

要转化为的类型(表达式)

举例:

package main

import "fmt"

func main() {

	var a int = 10
	var b int = 2

	fmt.Printf("a,b均为整型相除的结果 %d\n", a / b)
	fmt.Printf("a,b均为浮点型相除的结果 %f\n", float32(a) / float32(b))

}
a,b均为整型相除的结果 5
a,b均为浮点型相除的结果 5.000000

(十八)接口

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

格式:

// 定义接口
type 接口名 interface {
    方法名1 [返回类型]
    ...
}
// 定义结构体
type 结构体名 struct {
    变量
}
// 定义接口方法
func (结构体变量名 结构体名) 方法名1() [返回类型] {
    方法实现
}
...

举例:

package main

import "fmt"

type Phone interface {
	call()
}

type AmyPhone struct {

}
func (amyPhone AmyPhone) call() {
	fmt.Println("Hello, I am Amy!")
}

type TomPhone struct {

}
func (tomPhone TomPhone) call() {
	fmt.Println("Hello, I am Tom!")
}


func main() {

	var phone Phone

	phone = new(AmyPhone)
	phone.call()

	phone = new(TomPhone)
	phone.call()

}
Hello, I am Amy!
Hello, I am Tom!

(十九)错误

错误error是一个接口类型

举例:定义除0错误

package main

import (
	"fmt"
)

type DivError struct {
}
func (de *DivError) Error() string {
	str := "除数为0"
	return fmt.Sprintf(str)
}

func Divide(a int,  b int) (result int, errMsg string) {

	if b == 0 {
		dError := DivError{}
		errMsg = dError.Error()
		return
	} else {
		return a / b, ""
	}

}

func main() {

	// 正常
	if result, errMag := Divide(100, 10); errMag == "" {
		fmt.Println("100/10 = ", result)
	}
	// 异常
	if _, errMag := Divide(100, 0); errMag != "" {
		fmt.Println("100/0 = ", errMag)
	}

}

(二十)并发

通过go关键字开启goroutine

格式:

开启goroutine

go 函数名(参数列表)

举例:

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("hello") 
	say("world")

}

输出hello和world的顺序不定

world
hello
world
hello
hello
world
world
hello
hello
world

(二十一)通道

通道为用于传递数据的一个数据结构,可用于在两个goroutline间通过传递一个指定类型的值来同步运行和通讯,<-用于指定通道方向

创建通道:

ch := make(chan int)

通道可以设置缓冲区大小:

ch := make(chan int, 100)

遍历通道:

for i := range(ch) {
    fmt.Println(i)
}

关闭通道:

close(ch)

举例:

package main

import (
	"fmt"
)

func fib (n int, c chan int) {

	x, y := 0, 1
	for i := 0; i <n; i++ {
		c <- x
		x, y = y, x+y
	}
	close(c)
}


func main() {

	c := make(chan int, 10)
	go fib(cap(c), c)
	for i := range(c) {
		fmt.Println(i)
	}

}
0
1 
1 
2 
3 
5 
8 
13
21
34
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值