Go语言入门-新手村

golang新手村

文章目录

    • golang新手村
      • 序章
        • HelloWorld
        • HelloWorld Web版
      • 创建Golang世界的角色
        • 安装环境
        • 挑选合手的武器
      • 第一章 问世
      • 第二章 初识
          • 变量
          • 条件分支
          • 循环
      • 第三章 修行
          • 数组
          • 切片(slice)
          • hash表
          • make和new
      • 第四章 机遇
          • 函数
          • 指针
          • 指针和函数
          • 结构体
      • 第五章 出村--再出发


序章

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

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

Go语言入门-新手村_第1张图片

HelloWorld

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

Go语言入门-新手村_第2张图片
当然这个程序并没有什么其实和其他语言也差的不多
不过你可以慢慢看

HelloWorld Web版

Go语言入门-新手村_第3张图片
这就完成了一个Web服务器
你可以通过浏览器浏访问这个网页 端口为8080

Go语言入门-新手村_第4张图片
笔者之前是学习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语言入门-新手村_第5张图片
    在这里插入图片描述

安装环境

  • 首先你可以采用手动安装去官网下载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文件夹. 你所有的源代码都应该放在里面.
你就可以在这个里面创建你的工程项目了
Go语言入门-新手村_第6张图片
笔者这里创建了一个studyDemo的项目
go语言的文件希望你以.go结尾
在goland下创建一个main.go的文件
Go语言入门-新手村_第7张图片
第一行package声明你这个.go文件属于你这个项目中的哪一个包
你可以这样理解 比如你有两个123.txt, 但是它们俩是不一样的. 你怎么分清谁上谁呢?
给它们有一个命名空间就可以了 比如 这个上 A的123.txt 另外一个上B的123.txt 这样就分清楚了
这里的包的名字可以随意 但是最好和文件夹同名
任何一个项目需要运行都需要一个main的包
像下面这样 main这个函数前面有个箭头表示可以运行
main函数是你程序的唯一入口
Go语言入门-新手村_第8张图片
如果在demo1这个文件夹下 也有一个main.go但是 包不是main的话也无法运行
Go语言入门-新手村_第9张图片
import 可以引入其他包裹,
Go语言入门-新手村_第10张图片
多个包裹应该像下面这样引入
Go语言入门-新手村_第11张图片

当然你也可以引入你自己的
Go语言入门-新手村_第12张图片

Go语言入门-新手村_第13张图片

如果不是Goland 是终端 或者 cmd怎么去运行你的go程序
你可以使用go run 指令Go语言入门-新手村_第14张图片
或者你可以使用go build指令将代码编译成可执行程序
Go语言入门-新手村_第15张图片
当然你也可以使用go install


第二章 初识

变量

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

Go语言入门-新手村_第17张图片
在导入包的时候也会告诉你 没有使用这个包, 你导入了就应该使用它
Go语言入门-新手村_第18张图片
代码 2-1

package main

import "fmt"

func main() {
	var a int
	fmt.Printf("a = %d\n",a)
	var s string
	fmt.Printf("s = %s\n",s)
}

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

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

package main

import "fmt"

func main() {
	var a int = 5
	fmt.Printf("a = %d\n",a)
	var s string = "hello"
	fmt.Printf("s = %s\n",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 = %d\n",a)
	s := "hello"
	fmt.Printf("s = %s\n",s)
}

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

条件分支

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

package main

import "fmt"

func main() {
	a := 5
	fmt.Printf("a = %d\n",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 = %d\n",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 = %d\n",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)
	}
}

Go语言入门-新手村_第19张图片
首先说明一点 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)
}

Go语言入门-新手村_第20张图片

数组的下标从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])
}

在这里插入图片描述
Go语言入门-新手村_第21张图片

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

切片(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 = %d\n",cap(arr))
 }

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

Go语言入门-新手村_第22张图片
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 = %d\n",cap(arr))
	arr = append(arr, 7)
	fmt.Printf("newArr's cap = %d\n",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 = %d\n",cap(arr))
	arr = append(arr, 7)
	fmt.Printf("newArr's cap = %d\n",cap(arr))
	temp := arr[1:3]
	fmt.Println(temp)
 }

使用 [start:end] 可以对数组进行截取,截取的是使用地址截取 也就是共享了同一个数组
改变temp 也会改变arr
Go语言入门-新手村_第23张图片
代码 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 = %d\n",cap(arr))
	arr = append(arr, 7)
	fmt.Printf("newArr's cap = %d\n",cap(arr))
	temp := arr[1:3]
	fmt.Println(temp)
	temp[0] = 99
	fmt.Println(arr)
 }

Go语言入门-新手村_第24张图片
这里可以看到 改变了 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 = %d\n",cap(arr))
	arr = append(arr, 7)
	fmt.Printf("newArr's cap = %d\n",cap(arr))
	tempA := arr[:2]
	tempB := arr[1:]
	fmt.Println(tempA)
	fmt.Println(tempB)
 }

tempA是从arr的0 到 arr的2
tempB是从arr的1 到 arr的
Go语言入门-新手村_第25张图片
注意: 如果 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空指针错误
Go语言入门-新手村_第26张图片
出现这样的原因是因为没有给这个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)
 }

Go语言入门-新手村_第27张图片

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语言入门-新手村_第28张图片

第四章 机遇

函数

函数其实和高中的数学的函数是一个 概念 处理一个输入 输出的过程
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还提供了不定长参数
类似于下面代码
Go语言入门-新手村_第29张图片
在这里插入图片描述
看打印的结果
修改代码 如下 %T可以看到这个变量的类型
代码 4-5

package main

import "fmt"

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

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

打印的结果可以看到arg是一个slice
但是你如果想传一个切片过去 它会告诉你类型不匹配的错误
Go语言入门-新手村_第30张图片
但是你可以修改如下 则可正常打印
Go语言入门-新手村_第31张图片

指针

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

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

var a int
a = 5

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

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

Go语言入门-新手村_第33张图片
a = 5这条语句

Go语言入门-新手村_第34张图片
然后接下来接下一下
介绍一下正戏 接下来介绍的就是

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

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

Go语言入门-新手村_第36张图片
所以这里会把它置nil

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

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

Go语言入门-新手村_第37张图片

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

&a 得到的就是a前面的东西 也就是0x0043
然后把这个值给p
Go语言入门-新手村_第38张图片

再来感受一下 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
Go语言入门-新手村_第39张图片
Go语言入门-新手村_第40张图片

代码 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 它存储的是一级指针的地址
Go语言入门-新手村_第41张图片

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

Go语言入门-新手村_第42张图片

**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 = %p\n",&a)
}

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

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

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


package main

import "fmt"

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

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

Go语言入门-新手村_第43张图片

代码 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 --- %v\n",newDog, newDog)
	fmt.Printf("%T --- %v\n",newDogAddr, newDogAddr)
}

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

Go语言入门-新手村_第44张图片

当然它也等同于之前提到过的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 --- %v\n",newDog, newDog)
	fmt.Printf("%T --- %v\n",newDogAddr, newDogAddr)
}

Go语言入门-新手村_第45张图片

函数指针,一个结构体的属性也可以接受一个函数
代码 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社区等等都可以

你可能感兴趣的:(Golang)