Go语言入门-新手村 - Go语言中文社区

Go语言入门-新手村


golang新手村


序章

笔者本身是学习Java语言的, 从去年了解了Golang,今年正式学习Golang到现在对这门语言有着深深的热爱.
就想出一个教程帮助那些想学习Golang的学习爱好者.当然可能会有些不详细或者疏漏也希望大家能指出

  • go语言是由google开发的语言它的其中的一位作者就是C语言或者UNIX之父,它的开发阵容非常强大
  • go语言代码简单易读 但是功能非常强大,且执行速度超快 它被称为21世纪的C语言或者面向未来的语言
  • 这篇文章适合有一定基础的朋友,当然你要是0基础其实也可以,但是可能显得有些吃力.不过没关系,Golang的信条就是大道至简 这和笔者的信条及其的相似
  • 当然有人想说Golang的语法好丑啊, 其实笔者一开始也是这样认为的,丑帅丑帅的.但是到了后面发现Go的语法才是真的耐看
  • 接下来我想从简单的例子来带你进入Golang语言,这个时候看不懂也没有关系,会面会有详细教学

在这里插入图片描述

HelloWorld

最简单的程序当然就是Hello world!了

在这里插入图片描述
当然这个程序并没有什么其实和其他语言也差的不多
不过你可以慢慢看

HelloWorld Web版

在这里插入图片描述
这就完成了一个Web服务器
你可以通过浏览器浏访问这个网页 端口为8080

在这里插入图片描述
笔者之前是学习java的 到了Web那里你需要下载tomcat 然后进行一些列繁琐的配置,然后才可以使用
而Golang却非常的简单,它的http包是内置的,不需要引入任何的外部的数据
当然你可能觉得这些其他的语言也其实不用太麻烦 但是 Golang的特色并不在此
笔者认为Golang真正出彩是在它并发的处理还有接口
接下来会带你们走进Golang的世界,当然它真的很简单.如果有接触过C语言的人,Golang的关键字比C语言还要少,可能C语言你会被i++ 和 ++i这种操作困扰 但是Golang你完全不需要担心这个的问题


创建Golang世界的角色

首先的第一件事你应该要有一个GO的环境

  • 下图是笔者的操作系统环境
  • 使用了manjaro + i3 大道至简的Linux操作系统
  • go的版本使用的是 1.13 推荐使用 1.8及以上的版本
    在这里插入图片描述
    在这里插入图片描述

安装环境

  • 首先你可以采用手动安装去官网下载Go的环境
  • 当然国内也有镜像文件可以加速下载
  • manjaro的用户可以使用 : sudo pacman -S go
  • ubuntu的用户可以使用 apt-get
  • centos的用户可以使用 yum
  • win10的用户还是去网上找下教程吧 搜一下有很多 这里就不在赘述

挑选合手的武器

  • vim vim是一款超级好用的文本编辑器,装一些有关Golang的插件也是一个不错的选择,当然前提是你是一个使用vim的老手
  • Goland : 这是一个非常好用的编译器熟悉全家桶的朋友应该对这个不陌生.它提供相当智能的代码补全和提示.那么笔者就使用的是这个工具
  • 其他的工具这里不再赘述 应该还有很好的比如 vscode…

第一章 问世

go语言希望你把你的代码放在一个地方,这个地方就叫做工作目录.
**在LInux下你可以在终端输入(win10打开cmd) go env GOPATH显示你的工作目录 **
在这里插入图片描述
在你的go的工作目录下创建一个src文件夹. 你所有的源代码都应该放在里面.
你就可以在这个里面创建你的工程项目了
在这里插入图片描述
笔者这里创建了一个studyDemo的项目
go语言的文件希望你以.go结尾
在goland下创建一个main.go的文件
在这里插入图片描述
第一行package声明你这个.go文件属于你这个项目中的哪一个包
你可以这样理解 比如你有两个123.txt, 但是它们俩是不一样的. 你怎么分清谁上谁呢?
给它们有一个命名空间就可以了 比如 这个上 A的123.txt 另外一个上B的123.txt 这样就分清楚了
这里的包的名字可以随意 但是最好和文件夹同名
任何一个项目需要运行都需要一个main的包
像下面这样 main这个函数前面有个箭头表示可以运行
main函数是你程序的唯一入口
在这里插入图片描述
如果在demo1这个文件夹下 也有一个main.go但是 包不是main的话也无法运行
在这里插入图片描述
import 可以引入其他包裹,
在这里插入图片描述
多个包裹应该像下面这样引入
在这里插入图片描述

当然你也可以引入你自己的
在这里插入图片描述

在这里插入图片描述

如果不是Goland 是终端 或者 cmd怎么去运行你的go程序
你可以使用go run 指令在这里插入图片描述
或者你可以使用go build指令将代码编译成可执行程序
在这里插入图片描述
当然你也可以使用go install


第二章 初识

变量

你可以使用 var 关键字
var后面跟着变量名 变量类型
在这里插入图片描述
大致的变量表 当然你也可以在官网找到更详细的信息
在这里插入图片描述
这里有一行报错,它会告诉你没有使用变量a
对的这里不是一个提示或者警告 它是一个报错.
你现在如果想编译代码都是会被拒绝的, Go的作者及其看重代码的优化,如果你定义了一个变量却没有使用,那么这就是错误的

在这里插入图片描述
在导入包的时候也会告诉你 没有使用这个包, 你导入了就应该使用它
在这里插入图片描述
代码 2-1

package main

import "fmt"

func main() {
	var a int
	fmt.Printf("a = %dn",a)
	var s string
	fmt.Printf("s = %sn",s)
}

对于上述代码的输出
在这里插入图片描述
对于定义的变量int 的初始值是0, string类型是"" (空字符串)

修改代码2-1
代码 2-2

package main

import "fmt"

func main() {
	var a int = 5
	fmt.Printf("a = %dn",a)
	var s string = "hello"
	fmt.Printf("s = %sn",s)
	var str string
	str = "world"
	fmt.Println(str)
}

对于上述代码的输出
在这里插入图片描述
你可以通过命名的时候直接赋值 或者在之后赋值都可以
ps : 这里却是和其他的语言不一样 大部分的语言都是type name的形式 比如 int a, 而golang则是 a int.
一开始笔者也接受了好长时间, 后来仔细想想其实这种 方式更符合自然科学

当然Go语言提供了更简单的定义变量的形式
修改代码 2-2
代码 2-3

package main

import "fmt"

func main() {
	a := 5
	fmt.Printf("a = %dn",a)
	s := "hello"
	fmt.Printf("s = %sn",s)
}

这里使用了 := 来赋值 变量名 := 初始值
go会自动类型推断该变量上什么类型, 这是golang更推荐的一种形式
在这里插入图片描述

条件分支

go也有条件分支if else-if else
修改代码2-3
代码 2-4

package main

import "fmt"

func main() {
	a := 5
	fmt.Printf("a = %dn",a)
	
	if a > 10 || (a > 7 && a < 9) {
		fmt.Println("ok > 10")
	} else if a < 5 {
		fmt.Println("ok < 5")
	} else {
		fmt.Println("other")
	}
}

与很多语言不同的是 go的条件判断不需要括号()

在这里插入图片描述

对于go的选择分支还可以有局部变量

代码 2-5

package main

import "fmt"

func main() {
	a := 5
	fmt.Printf("a = %dn",a)

	if k := 3; a ^ 13 == 6 {
		fmt.Println("ok")
	} else {
		fmt.Println("k = ", k)
	}
}

k := 3是这个条件分支的局部变量 只可以在这个条件分支里面使用 ,当然也可以有多个变量
代码 2 - 6

package main

import "fmt"

func main() {
	a := 5
	fmt.Printf("a = %dn",a)

	if k,b := 3,6; a ^ 13 == 6 {
		fmt.Println("ok")
	} else {
		fmt.Println("k = ", k)
		fmt.Println("b = ", b)
	}
}

代码 2-7

package main

import "fmt"

func main() {
	var a int = 1
	switch a {
	case 1: a++
	case 2: a++
	case 3: a++
	default: a = 9
	}
	fmt.Println(a)
}

那大家可以想想这个结果是几
那有一定编程经验的人可能会说上9, 因为没有break的话case分支会慢慢下去,并不是
它只是执行了case 1的分支就结束了
在这里插入图片描述
这里是golang做的很好的一个点, 我们在使用switch-case的时候,更多的时候可能只会用到一个分支,
难免会有忘写break的时候,所以有时候可能会导致逻辑上的错误.所以Go你认为它会默认的写上break语句
当然你也可以抹掉这个break

使用fallthrough 穿透case

代码 2-8

package main

import "fmt"

func main() {
	var a int = 1
	switch a {
	case 1: a++
	fallthrough
	case 2: a++
	fallthrough
	case 3: a++
	fallthrough
	default: a = 9
	}
	fmt.Println(a)
}

这里穿透了case 也就是说会往下运行

在这里插入图片描述
当然这里其实还有一个switch-type的使用,但是在这里不应该涉及更深的 后面讲

循环

golang提供的循环就只有for关键字, 先来看一个例子

代码 2-9

package main

import "fmt"

func main() {
	for i := 0; i < 5; i++ {
		fmt.Println(i)
	}
}

在这里插入图片描述
首先说明一点 golang 只有for循环并没有while循环
for循环同样上没有圆括号的, 它通过两个分号分出了三块

  • 第一块 也就是i := 0 循环的初始化,在没有开始循环的时候只会执行一次
  • 第二块 i < 5 上每次循环开始的判断条件, 它决定了你的循环是否结束
  • 第三块 i++ 是每次循环执行结束执行的语句

代码 2-10

package main

import "fmt"

func main() {
	i := 0
	for ; i < 5;  {
		fmt.Println(i)
		i++
	}
}

2-9的代码也可以这样写

代码 2- 11

package main

import "fmt"

func main() {
	i := 0
	for  i < 5  {
		fmt.Println(i)
		i++
	}
}

代码 2- 10 和 代码2 - 11等价

第三章 修行

数组

golang也提供了数组(元组)

代码 3 - 1

package main

import "fmt"

func main() {
	var arr [3]int
	fmt.Println(arr)
}

在这里插入图片描述

这里的arr是在连续的内存中放了3个int类型的元素, 注意这个地址上连续的.
当然也可以使用 := 来定义一个数组

代码 3-2

package main

import "fmt"

func main() {
	arr := [3]int{}
	fmt.Println(arr)
}

当然也可以在定义的时候赋初值
代码 3-3

package main

import "fmt"

func main() {
	arr := [3]int{3,6,9}
	fmt.Println(arr)
}

在这里插入图片描述

数组的下标从0开始, arr 这个数组的访问应该上0,1,2
也可以在初始值的时候给某个位置赋值

代码 3-4

package main

import "fmt"

func main() {
	arr := [3]int{1:5}
	fmt.Println(arr)
}

这时候只给第二个元素赋值为5,其他元素都上初始值
在这里插入图片描述
代码 3 - 5
通过数组的下标来访问数组

package main

import "fmt"

func main() {
	arr := [3]int{1:5}
	fmt.Println(arr)
	arr[0] = 9
	fmt.Println(arr)
	fmt.Println(arr[1])
}

在这里插入图片描述
在这里插入图片描述

如果 你访问了数组元素以外的位置会给你报错

切片(slice)

数组的缺点就是你没有办法去改变数组的大小.Golang提供了一个切片的东西.
它其实就是一个自动扩容的数组
它的定义是这样的

代码 3 - 6

package main

import "fmt"

func main() {
	arr := []int{1:5}
	fmt.Println(arr)
	arr[0] = 9
	fmt.Println(arr)
	fmt.Println(arr[1])
	fmt.Printf("arr's cap = %dn",cap(arr))
 }

它和之前代码3-5之前不同的就上arr后面没有了数组的大小

在这里插入图片描述
cap是这个切片的容量

但是你可以清晰的看到这个数组的大小其实是2,但其实它是可以向这个数组添加的

代码 3 - 7

package main

import "fmt"

func main() {
	arr := []int{1:5}
	fmt.Println(arr)
	arr[0] = 9
	fmt.Println(arr)
	fmt.Println(arr[1])
	fmt.Printf("arr's cap = %dn",cap(arr))
	arr = append(arr, 7)
	fmt.Printf("newArr's cap = %dn",cap(arr))
 }

使用append的内置函数 可以像一个切片里添加一个元素, 如果切片满了则会新开一个数组,这个数组的大小上以前的两倍

代码 3-8

package main

import "fmt"

func main() {
	arr := []int{1:5}
	fmt.Println(arr)
	arr[0] = 9
	fmt.Println(arr)
	fmt.Println(arr[1])
	fmt.Printf("arr's cap = %dn",cap(arr))
	arr = append(arr, 7)
	fmt.Printf("newArr's cap = %dn",cap(arr))
	temp := arr[1:3]
	fmt.Println(temp)
 }

使用 [start:end] 可以对数组进行截取,截取的是使用地址截取 也就是共享了同一个数组
改变temp 也会改变arr
在这里插入图片描述
代码 3-9

package main

import "fmt"

func main() {
	arr := []int{1:5}
	fmt.Println(arr)
	arr[0] = 9
	fmt.Println(arr)
	fmt.Println(arr[1])
	fmt.Printf("arr's cap = %dn",cap(arr))
	arr = append(arr, 7)
	fmt.Printf("newArr's cap = %dn",cap(arr))
	temp := arr[1:3]
	fmt.Println(temp)
	temp[0] = 99
	fmt.Println(arr)
 }

在这里插入图片描述
这里可以看到 改变了 temp的第一个也会改变arr相应的元素

当然也可以省略start 或者 end 比如下面代码

package main

import "fmt"

func main() {
	arr := []int{1:5}
	fmt.Println(arr)
	arr[0] = 9
	fmt.Println(arr)
	fmt.Println(arr[1])
	fmt.Printf("arr's cap = %dn",cap(arr))
	arr = append(arr, 7)
	fmt.Printf("newArr's cap = %dn",cap(arr))
	tempA := arr[:2]
	tempB := arr[1:]
	fmt.Println(tempA)
	fmt.Println(tempB)
 }

tempA是从arr的0 到 arr的2
tempB是从arr的1 到 arr的
在这里插入图片描述
注意: 如果 cap改变说明数组进行了扩容了, 得到的地址就应该是新扩容的数组了,
Go也有垃圾回收机制,如果不再有引用旧数组了,在发生垃圾回收的时候可能会回收旧数组的空间.

hash表

hash表是一个键值对的结构.可以通过key找到对应的value.
它是一个查询和插入效率非常高的数据结构, 它的理念就是通过空间换时间.

代码 3-10

package main

import "fmt"

func main() {
	var m map[string]int
	fmt.Println(m)
 }

在这里插入图片描述
go语言的hash表也是可以通过像数组下标的访问形式来访问hash表.

接下来先看一个错误的代码

代码 3-11

package main

import "fmt"

func main() {
	var m map[string]int
	fmt.Println(m)
	m["hello"] = 5
	m["world"] = 7
	fmt.Println(m)
 }

运行会得到这样的一个错误 nil空指针错误
在这里插入图片描述
出现这样的原因是因为没有给这个hash表分配内存

代码 3 -12

package main

import "fmt"

func main() {
	var m map[string]int = map[string]int{}
	m["hello"] = 5
	m["world"] = 7
	fmt.Println(m)
 }

可以通过这样的方式给这个map进行初始化
在这里插入图片描述
对于m访问的话也可以通过类似于数组下标的形式

代码 3 - 13

package main

import "fmt"

func main() {
	var m map[string]int = map[string]int{}
	m["hello"] = 5
	m["world"] = 7
	fmt.Println(m)
	fmt.Println(m["hello"])
	fmt.Println(m["aaa"])
 }

在这里插入图片描述
删除一个键值对 只需要删除键就可以了
代码 3 - 13 -1

package main

import "fmt"

func main() {
	var m map[string]int = map[string]int{}
	m["hello"] = 5
	m["world"] = 7
	fmt.Println(m)
	fmt.Println(m["hello"])
	fmt.Println(m["aaa"])
	delete(m, "hello")
	fmt.Println(m)
 }

在这里插入图片描述

make和new

先来看new 它是一个内建函数(函数的内容放在下一张讲, 其实高中就学过函数本就是一个处理输入输出的过程 main函数也是一个函数)
它的返回值是一个指针, 指针也放下下一个章节讲解

func new(Type) *Type

官方对于 new 关键字的定义如下:
内建函数 new 用来分配内存,它的第一个参数是一个类型,不是一个值,它的返回值是一个指向新分配类型零值的指针
这个命令等到指针的时候在详细讲解一下

make指令一般用来为切片slice 字典 map 管道(chan 后面讲) 第二个是大小

func make(Type, size IntegerType) Type

其实这里返回值前面没有*也就是指针 但是slice map chan本身就是指针类型后面说

代码 3 - 14

package main

import "fmt"

func main() {
	var m map[string]int
	m = make(map[string]int) //这里也可以省略大小
	m["hello"] = 5
	m["world"] = 7
	fmt.Println(m)
	fmt.Println(m["hello"])
	fmt.Println(m["aaa"])
	arr := make([]int, 5)
	fmt.Println(arr)
 }

在这里插入图片描述

第四章 机遇

函数

函数其实和高中的数学的函数是一个 概念 处理一个输入 输出的过程
Go语言使用func的关键字来表示一个函数,然后是函数的参数列表, 返回值
代码 4-1

func add(a int, b int) int {
	sum := a + b
	return sum
}

简单的解释一下这个代码其实就是返回两个数的和的函数
这是一个名字为add的函数, 它接受两个参数 a和b都是int型当然这里也可以像如下的写法

func add(a, b int) int

它返回一个int型的数据.
函数的过程就是使用一个临时变量sum 接受 a+b的数据,返回这个数的数据

Go语言接受多返回值,可以用圆括号加起来.
代码 4 - 2

package main

import "fmt"

func fn(a, b int) (int, int) {
	return a + b, a * b
}

func main() {
	n1, n2 := fn(3 , 4)
	fmt.Println(n1, n2)
 }

在这里插入图片描述
这里返回了两个数的和和积.
在别的函数里可以调用这个函数 方式是函数名字(参数1,参数2,参数…).
当然也可以给两个返回值起个名字

代码 4 -3

func fn(a, b int) (n1, n2 int) {
	n1 = a + b
	n2 = a * b
	return 
}

像上面这样 相当于给返回值起了个名字 然后需要返回的时候直接返回就可以.

代码 4-4

package main

import "fmt"

func fn(a, b int)  {
	fmt.Println(a,b)
}

func main() {
	fn(3,4)
 }

当然也可以没有返回值

Go还提供了不定长参数
类似于下面代码
在这里插入图片描述
在这里插入图片描述
看打印的结果
修改代码 如下 %T可以看到这个变量的类型
代码 4-5

package main

import "fmt"

func fn(a, b int, arg ...int)  {
	fmt.Println(a,b, arg)
	fmt.Printf("%Tn", arg)
}

func main() {
	fn(3,4,3,4,5,6)
 }

打印的结果可以看到arg是一个slice
但是你如果想传一个切片过去 它会告诉你类型不匹配的错误
在这里插入图片描述
但是你可以修改如下 则可正常打印
在这里插入图片描述

指针

指针一直是一个让程序员又爱又恨的东西,是因为它很难用却又很好用.
难用是因为它恨抽象.而且又有些不安全.
好用是因为它的功能很强大.
那笔者先来介绍一下这个概念把

简单打个比喻. 人作为唯一存在的证明其实可以用身份证来标识.因为身份证每个人都上不同的.
那你平时定义一个变量,比如变量a
那么可能是函数fn的变量a 可能是主函数的变量a 你能区分这两个a 你的电脑怎么区分呢?
你的电脑其实并不是这样识别的. a只是方便你程序员去操作.而在系统中则使用的是地址. 就类似于你的身份证. 操作系统通过地址来使用你的变量

var a int
a = 5

简单的看一下 上述 这个过程
在连续存储的这个内存空间中找到了一个地址 比如 0x0045(这里只是简单的举例了一下这个地址.实际的要比这复杂 ,这里使用16进制来表示一个地址 以0x开头)
这里可能会有一些随机值.
在这里插入图片描述

然后给0x0045这个空间赋初始值0

在这里插入图片描述
a = 5这条语句

在这里插入图片描述
然后接下来接下一下
介绍一下正戏 接下来介绍的就是

  • p *int
  • &取地址符
  • *指向运算
    先说*int 这个其实是一个指针变量 它存的是一个地址.一个int类型变量的地址 来看一下这个过程

这里p的里面是一个随机值.为随便写的可能不是这个
在这里插入图片描述
go语言会给p做一个初始化. 它会指向nil. 就上空指针.如果知道C语言的朋友可能经常会出现野指针.
因为C语言不会做初始化工作 也就上说这里的情况.
这里p可以通过自己访问到0x0043的内容可以修改0x0043的的值.
试想一下 如果你忘记初始化就使用了. 那么很有可能这里的地址指向了你硬盘空间的大小
你无意识的改了一下(假设你的系统让你改 实际上也会有保护机制) 改成了2 那你硬盘1个T改成了2个G

在这里插入图片描述
所以这里会把它置nil

接下来解释一下下面的代码

var a int = 5
var p * int
p = &a

在这里插入图片描述

最主要的是第三行代码
&a 就是取到a的地址 

&a 得到的就是a前面的东西 也就是0x0043
然后把这个值给p
在这里插入图片描述

再来感受一下 int *p存储的就是int类型变量的地址.我p可以通过访问为存储的地址,访问到那个地址的变量
比如说 我知道你的身份证为就能找到你是一样的.

最后看一下 *p操作

代码 4-6

import "fmt"

func main() {
	var a = 5
	var p *int
	p = &a
	*p = 99
	fmt.Println(a)
}

在这里插入图片描述

*p 就是找到p存储的地址的值 p存储的是0x0043 然后找到0x0043对应的值 修改成99
在这里插入图片描述
在这里插入图片描述

代码 4-7

package main

import "fmt"

func main() {
	var a = 5
	var p *int
	var q **int
	p = &a
	*p = 99
	fmt.Println(a)
	q = &p
	**q = 123
	fmt.Println(a)
}

接下来 对二级指针举例说明一下
在这里插入图片描述
对于二级指针q 它存储的是一级指针的地址
在这里插入图片描述

&p 得到的就是p的地址 也就是0x0045

在这里插入图片描述

**q

第一个*是取到了0x0045的那个数据这里也就是p,再取一次*则取到了 0x0043的位置的数据 也就是a然后赋值给99

简单总结一下 &a 就是取到上图中变量a前面代表的地址 *p就是取这个指针类型变量存储地址所对应的变量

指针和函数

前面讲了指针和函数.其实这两个是一套很好的组合拳.这两个搭配在一起会有奇效
代码 4-8

package main

import "fmt"

func fn(a int)  {
	a = 77
}

func main() {
	a := 8
	fn(a)
	fmt.Println(a)
}

考虑下上述代码的输出值, 有经验的朋友可能会说是8, 其实也确实是8
在这里插入图片描述
因为这里两个不是同一个地址

代码 4-9

package main

import "fmt"

func fn(a int)  {
	a = 77
	fmt.Printf("fn a addr = %pn",&a)
}

func main() {
	a := 8
	fn(a)
	fmt.Println(a)
	fmt.Printf("main a addr = %pn",&a)
}

这里可以看到main的地址和fn的地址是不一样的.其实对于基本类型都是会复制一个副本 并不会改变本体的值
在这里插入图片描述

如果想在fn改变变量a的值.有经验的朋友肯定会说传地址
代码 4-10


package main

import "fmt"

func fn(a *int)  {
	*a = 77
	fmt.Printf("fn a addr = %pn",&a)
	fmt.Printf("fn-main a addr = %pn", a)
}

func main() {
	a := 8
	fn(&a)
	fmt.Println(a)
	fmt.Printf("main a addr = %pn",&a)
}

在这里插入图片描述

代码 4-11

package main

import "fmt"

func fn(a [5]int)  {
	for i := 0; i < len(a); i++ {
		a[i] = i
	}
}

func main() {
	arr := [5]int{}
	fn(arr)
	for i := 0; i < len(a); i++ {
		fmt.Println(arr)
	}
}

对于上述代码可能有开发经验的人会说这个打印结果上0-4.
但是其实并不是
在这里插入图片描述
在go语言里数组的传递也是值的copy 如果想要修改的话 这里推荐使用切片

代码 4 -11

package main

import "fmt"

func fn(a []int)  {
	for i := 0; i < len(a); i++ {
		a[i] = i
	}
}

func main() {
	arr := make([]int, 5)
	fn(arr)
	fmt.Println(arr)
}

在这里插入图片描述
对于map 管道也是指针的传递

结构体

它的格式是

type (name) struct {
}

例如定义一个 dog, 它有两个属性 name和age

type dog struct {
	age int
	name string
}

你可以通过如下代码给它赋初始值
代码 4-12

package main

import "fmt"

type dog struct {
	age int
	name string
}

func main() {
	newDog := dog{age : 3, name:"kk"}
	fmt.Println(newDog)
}

当然 age 和 name也可以省略它会上默认的初始值
你也可以通过变量.属性来访问 如下列代码
代码 4-13

package main

import "fmt"

type dog struct {
	age int
	name string
}

func main() {
	newDog := dog{age : 3, name:"kk"}
	fmt.Println(newDog)
	newDog.age = 7
	fmt.Println(newDog)
}

在这里插入图片描述

修改代码4-13
代码 4 -14

package main

import "fmt"

type dog struct {
	age int
	name string
}

func main() {
	newDog := dog{age : 3, name:"kk"}
	fmt.Println(newDog)
	newDog.age = 7
	fmt.Println(newDog)
	newDogAddr:= &dog{age:8, name:"tt"}
	fmt.Printf("%T --- %vn",newDog, newDog)
	fmt.Printf("%T --- %vn",newDogAddr, newDogAddr)
}

newDogAddr是指向dog类型的一个指针
%v是打印这个类型的值

在这里插入图片描述

当然它也等同于之前提到过的new
代码 4-15

package main

import "fmt"

type dog struct {
	age int
	name string
}

func main() {
	newDog := dog{age : 3, name:"kk"}
	fmt.Println(newDog)
	newDog.age = 7
	fmt.Println(newDog)
	newDogAddr := new(dog)
	fmt.Println(newDogAddr)
	newDogAddr.name = "pp"
	newDogAddr.age = 15
	fmt.Printf("%T --- %vn",newDog, newDog)
	fmt.Printf("%T --- %vn",newDogAddr, newDogAddr)
}

在这里插入图片描述

函数指针,一个结构体的属性也可以接受一个函数
代码 4-16

package main

import "fmt"

type dog struct {
	age int
	name string
	show func(int,string) string
}

func showMe(age int, name string) string {
	return fmt.Sprintln("我今年", age ,"岁了, 我叫:",name)
}

func main() {
	newDog := dog{age: 3, name: "kk"}
	newDog.show = showMe
	str := newDog.show(newDog.age, newDog.name)
	fmt.Println(str)
}

newDog接受的show属性接受一个有一个int参数 string参数 并且返回一个string类型的函数.
showMe就正好满足
在这里插入图片描述


第五章 出村–再出发

基础的已经大概讲完了.可能有些东西讲的不是很细.也希望大家能提出来我会慢慢完善.
然后下一篇博客我会讲方法 协程 管道 闭包 延时调用等等等
下一章地址 : 还没有写等我写完会回添的
推荐读物 : 目前的基础阶段 首先官网肯定是最权威的这是一个首推
然后还有例如四十二章经
Go社区等等都可以

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_42011541/article/details/103314781
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢