导入包的使用
方式一:
package main
import "fmt"
import "os"
func main () {
fmt.Println("this is a test")
fmt.Println("os.Args = ", os.Args)
}
方式二:(常用)
package main
import (
"fmt"
"os"
)
func main () {
fmt.Println("this is a test")
fmt.Println("os.Args = ", os.Args)
}
. 操作 调用函数,无需通过包名,但容易导致重名问题
package main
import . "fmt"
import . "os"
func main () {
Println("this is a test")
Println("os.Args = ", Args)
}
给包取别名
package main
import io "fmt"
func main () {
io.Println("this is a test")
}
忽略此包
package main
import _ "fmt"
func main () {
}
所有用go语言编译的可执行程序都必须有一个叫main的包,一个可执行程序有且仅有一个main包
package main 必须包含一个 main 函数
工程管理:同级目录
1、分文件编程(多个源文件),必须放在src目录
2、设置GOPATH环境变量
3、同一个目录,包名必须一样
4、go env 查看 go 相关的环境路径
5、同一个目录,调用别的文件的函数,直接调用即可,无需包名应用
工程管理:不同目录
1、不同目录,包名不一样
2、调用不同包里面的函数,格式:包名 . 函数名()
3、调用别的包的函数,这个包函数名字如果首字母是小写,无法让别人调用,要想别人能调用,必须首字母大写
init 函数
_ 操作
有时,用户可能需要导入一个包,但是不需要引用这个函数的标识符。在这种情况,可以使用空白标识符 _ 来重命名这个导入:
import (
_ "fmt"
)
_ 操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数
分类
指针计本操作
每个变量有两层含义:变量的内存,变量的地址
var a int = 10
fmt.Printf("a = %d\n", a) //变量的内存
fmt.Printf("a = %v\n", &a)
保存某个变量的地址,需要指针类型 *int 保存int的地址,**int 保存 *int 地址
声明(定义),定义只是特殊的声明
定义一个变量p,类型为 *int
var p *int
p = &a //指针指向谁,就把谁的地址赋值给指针变量
fmt.Printf("p = %v, &a = %v\n", p, &a)
*p = 666 //*p操作的不是p的内存,是P所指向的内存(就是a)
fmt.Printf("p = %v, &a = %v\n", p, &a)
不要操作没有合法指向的内存
package main
import "fmt"
func main () {
var p *int
p = nil
fmt.Printf("p = ", p)
*p = 666 //err,因为p没有合法指向
}
new函数的使用
package main
import "fmt"
func main() {
var p *int //指向一个合法内存,p = &a
p = new(int) //p是*int,指向int类型
*p = 666
fmt.Printf("p = ", *p)
}
自动推导类型
q := new(int)
*q = 777
fmt.Printf("*q = ", *q)
go语言保留了指针,但与其他语言不同的是:
1、指针的默认值是nil,没有null常量
2、操作符 & 去变量的地址,* 通过指针访问目标对象
3、不支持指针运算,不支持 -> 运算符,直接用 . 访问目标成员
注意:不要操作没有合法指向的内存;未初始化或赋值为 nil 的指针,否则会报错
用 new()函数来生成空间用于操作内存,类似于动态分配空间,但go语言不需要考虑释放空间
普通变量做函数参数
package main
import "fmt"
func swap(a, b int) {
a, b = b, a
fmt.Printf("swap: a = %d, b = %d\n", a, b)
}
func main (){
a, b := 10, 20
//通过一个函数交换a和b的内容
swap(a, b) //变量本身传递,值传递(站在变量角度)
fmt.Printf("main: a = %d, b = %d\n", a, b)
}
swap: a = 20, b = 10
main: a = 10, b = 20
由于变量作用域不同,定义不同,即出现的结果:自定义函数内部变量值发生;饿变化,进行了值传递,但在住函数之中并没有交换变量的值
指针做函数参数
import "fmt"
func swap(p1, p2 int) {
p1, p2 = p2, p1
}
func main (){
a, b := 10, 20
//通过一个函数交换a和b的内容
swap(&a, &b) //地址传递
fmt.Printf("main: a = %d, b = %d\n", a, b)
}
main: a = 20, b = 10
交换了两个变量的内存,使主函数之中实现了变量值得交换。
数组
数组是同一个类型的集合
元素必须是常量
【数字】,这个数字作为元素个数
注意:数组定义时,指定的数组元素个数必须是常量
操作数组元素,从0开始,到len()-1,不对称元素,这个数字,叫下标
下标可以是变量或常量
a[0] = 1
i := 1
a[i] = 2 //a[1] = 2
package main
import "fmt"
func main() {
var id [50]int
//操作数组,通过下标,从零开始,到len()-1
for i := 0; i < len(id); i++ {
id[i] = i + 1
fmt.Printf("id[%d] = %d\n", i, id[i])
}
}
遍历,打印下标,返回元素`
for i := 0; i < len(a); i++ {
a[i] = i + 1
}
for i, data := range a {
fmt.Printf("a[&d = %d\n]", i, data)
}
数组的初始化(声明定义同时赋值,叫初始化)
1、全部初始化
var a [5]int = [5]int{1,2,3,4,5}
a = [1 2 3 4 5]
简洁写法:
a := [5]int{1,2,3,4,5}
a = [1 2 3 4 5]
2、部分初始化,没有初始化的元素,自动复制为0
a := [5]int{1,2,3}
fmt.Println("a = ", a)
a = [1 2 3 0 0]
指定某个元素初始化
a := [5]int{2: 10, 4: 20}
fmt.Println("a = ", a)
a = [0 0 10 0 20]
有多少个[]就是多少维
有多少个[]就用多少个循环
二维数组
var a [3][4]int
k := 0
for i := 0; i < 3; i++{
for j := 0; j < 4; j++{
k++
a[i][j] = k
fmt.Printf("a[%d][%d] = %d, ", i, j, a[i][j])
}
fmt.Printf("\n")
}
fmt.Println("a = ", a)
有3个元素,每个元素又是一维数组[4]int
a := [3][4]int{{1,2,3,4},{5,6,7,8},{9,10,11,12}}
数组的比较和赋值:
比较只能用==或者!=,返回布尔变量
只能比较同样的数组类型是不是每个元素都一样
同类型的数组可以赋值
随机数的使用
设置种子,只需一次
rand.Seed(123)
产生随机数
fmt.Println(rand.Int())
如果种子参数一样,每次运行程序产生的随机数都一样
package main
import "fmt"
import "math/rand"
import "time"
func mian(){
rand.Seed(time.Now().UnixNano()) //以当前系统时间作为种子参数
for i := 0; i < 5 ; i++{
fmt.Println("rand = ", rand.Intn(100)) //限制在100内的数
}
}
冒泡排序
挨着两个元素比较
升序,大于交换
package main
import "fmt"
import "math/rand"
import "time"
func main() {
rand.Seed(time.Now().UnixNano()) //设置时间种子
var a [10]int
n := len(a)
for i := 0; i < n; i++ {
a[i] = rand.Intn(100) //生成100以内随机数
fmt.Printf("%d,", a[i])
}
fmt.Println("\n")
//冒泡排序,相邻两元素比较排序,升序,大则交换
for i := 0; i < n-1; i++ {
for j := 0; j < n-1-i; j++ {
if a[j] > a[j+1] {
a[j], a[j+1] = a[j+1], a[j]
}
}
}
fmt.Println("\n排序完成:\n"){
for i :=0; i < n; i++ {
fmt.Printf("%d, ", a[i])
}
fmt.Printf("\n")
}
数组做函数参数
数组做函数参数,它是值传递
实参数组的每一个元素给形参数组拷贝一份
形参的数组是实参数组的复制品
package main
import "fmt"
func modify(a [5]int){
a[0]=10
fmt.Println("modify a = ", a)
}
func main(){
a := [5]int{1,2,3,4,5} //初始化
modify(a)
fmt.Println("main a = ", a)
}
数组指针做函数参数
p指向实现数组a,它是指向数组,它是数组指针
*p代表指针所指向的内存,就是实参a
package main
import "fmt"
func modify(p *[5]int){
(*p)[0]=10
fmt.Println("modify a = ", *p)
}
func main(){
a :=[5]int{1,2,3,4,5}
modify(&a)
}
数组的大小是固定的,在参数传递时必须完整传递,故引入切片的概念和功能来弥补数组的缺点。同时切片也可以理解为变长的动态数组。切片不指定长度,从数组切入。
low:切片下标的起点
high:切片下标的终点(不包括)
切片的长度len=high-low
切片的容量cap=max-low
切片的初始化
a := []int{1,2,3,0,0} //中括号内可以省略...
切片和数组的区别:
1、数组[]长度是固定的一个常量,不能修改长度,长度和容量都是那个常量。
2、切片,[]里面为空或…,切片的长度和容量可以不固定。
3、append(s,11) 可为切片s末尾追加元素11。
切片的创建:
自动推导类型
s1 := []int{1,2,3,4}
借助make函数,格式make(切片类型,长度,容量)
s2 := make([]int, 5, 10)
没有指定容量,容量和长度
s3 := make([]int, 5)
切片的截取
package main
import "fmt"
func main() {
array:=[]int{1,2,3,4,5,6,7,8,9}
//[low:high:max]取下标从low开始的元素,len=high-low, cap=max-low
s1 :=array[:] //[0:len(array):len(array)]不指定容量和长度一样
fmt.Println("s1 = ", s1)
fmt.Printf("len = %d, cap = %d\n", len(s1), cap(s1))
//操作某个元素,和数组操作方式一样
data := array[1]
fmt.Println("data = ", data)
s2 :=array[3:6:7] //a[3], a[4],a[5] len = 6-3=3 cap = 7-3=4
fmt.Println("s2 = ", s2)
fmt.Printf("len = %d, cap = %d\n", len(s2), cap(s2))
s3 :=array[:6] //从0开始取6个元素,长度和容量都是6,此方法最为常用
fmt.Println("s3 = ", s3)
fmt.Printf("len = %d, cap = %d\n", len(s3), cap(s3))
s4 :=array[3:] //从array[3]开始,取到结尾
}
切片和底层数组关系
package main
import "fmt"
func main(){
a := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
//新切片
s1 := a[2:5] //从a[2]开始,取3个元素
fmt.Peintln("s1 = ", s1)
//另外新切片
s2 := s1[2:7]
s2[2] = 777
fmt.Println("s2 = ", s2)
fmt.Println("a = ", a)
}
注意:切片指向底层数组,在对某个切片元素重新赋值时,无论切片经过多少次重切,切片所指向的最底层数组的相应元素也会跟着被重新赋值,改变
append函数的使用
package main
import "fmt"
func mian(){
s1 := []int{}
fmt.Printf("len = %d, cap = %d\n", len(s1), cap(s1))
fmt.Println("s1 = ", s1)
//在原切片的末尾添加元素
s1 = append(s1, 1)
s1 = append(s1, 2)
s1 = append(s1, 3)
fmt.Printf("len = %d, cap = %d\n", len(s1), cap(s1))
fmt.Println("s1 = ", s1)
s2 := []int{1, 2, 3}
fmt.Println("s2 = ", s2)
s2 = append(s2, 5)
s2 = append(s2, 5)
s2 = append(s2, 5)
fmt.Println("s2 = ", s2)
}
append扩容特点
如果超过原来的容量,通常以2倍容量扩容
package main
import "fmt"
func mian(){
s := make([]int, 0, 1) //容量为1
oldCap := cap(s)
for i := 0;i < 20; i++{
s = append(s, i)
if newCap := cap(s); oldCap < newCap {
fmt.Printf("cap: %d ===> &d\n", oldCap, newCap)
oldCap = newCap
}
}
}
copy的使用
package main
import "fmt"
func mian(){
srcSlice := []int{1, 2}
dstSlice := []int{6, 6, 6, 6, 6}
copy(dstSlice, srcSlice)
fmt.Println("dst = ", dstSlice)
}
切片做函数参数
引用传递,传递方式与数组值传递相同,但不会完整传递,只会传递所需的某个元素
package main
import "fmt"
import "math/rand"
import "time"
func InitData(s []int) {
//设置种子
rand.Seed(time.Now().UnixNano())
for i := 0; i < len(s); i++ {
s[i] = rand.Intn(100) //生成100以内随机数
}
}
//冒泡排序
func BubbleSort(s []int){
n := len(s)
for i := 0; i < n-1; i++{
for j := 0; j < n-1-i; j++{
if s[j] > s[j+1]{
s[j], s[j+1] = s[j+1], s[j]
}
}
}
}
func mian(){
n := 10
//创建一个切片,len为n
s := make([]int, n)
InitData(s) //初始化数组
fmt.Println("排序前:", s)
BubbleSort(s) //冒泡排序
fmt.Println("排序后:", s)
}
一个4位数取出每一位数
func GetNum(s []int, num int){
s[0] = num / 1000 //取千位
s[1] = num % 1000 / 100 //取百位
s[2] = num % 100 / 10 //取十位
s[3] = num % 10 //取个位
}
map的介绍
map(映射、字典)是一种内置的数据结构,它是一个无序的key-value对的集合
map格式:map[key类型]value类型{}
在一个map里所有的键都是唯一的,而且必须是支持==和!=操作符的类型,切片、函数以及包含切片的结构类型这些类型由于具有引用语义,不能作为映射的键,使用这些类型会造成编译错误
map的基本使用
package main
import "fmt"
func main(){
//定义一个变量,类型为map[int]string
var m1 map[int]string
fmt.Println("m1 = ", m1)
//对于map只有len,没有cap
fmt.Println("len = ", len(m1))
//可以通过make创建
m2 := make(map[int]string)
fmt.Println("m2 = ", m2)
fmt.Println("len = ", len(m2))
//可以通过make创建,可以指定长度,只有指定了容量, 但是里面却是一个数据也没有
m3 := make(map[int]string, 2)
m3[1] = "mike" //元素的操作
m3[2] = "go"
m3[3] = "c++"
fmt.Println("m3 = ", m3)
fmt.Println("len = ", len(m3))
}
键值是唯一的
m4 := map[int]string{1:"mike", 2:"go", 3:"c++"}
fmt.Println("m4 = ", m4)
map的赋值:
赋值,如果已经存在的key值,修改内容
m[1]=”c++”
m[3]=”go” //追加,map底层自动扩容,和append类似
map的遍历:
第一个返回值为key,第二个返回值为value,遍历结果是无序的
for key,value:=range m{}
判断key是否存在:
第一个返回值为key所对应的value,第二个返回值为key是否存在的条件,存在ok为true
value, ok :=m[0]
if ok == true {
fmt.Println("m[1] = ", value)
}else{
fmt.Println("key不存在")
}
}
map删除
delete(m,1) //删除key为1的内容
map做函数参数
package main
import "fmt"
func test(m map[int]string){
delete(m, 1)
}
func main(){
m := map[int]string{1:"map", 2:"yo", 3:"go"}
fmt.Println("m = ", m)
test(m) //在函数内部删除某个key
fmt.Println("m = ", m)
}
结构体类型
结构体普通变量初始化
package main
import "fmt"
//定义一个结构体类型
type Student struct{
id int
name string
sex byte //字符类型
age int
addr string
}
func main(){
//顺序初始化,每个成员必须初始化
var s1 Student = Student{1,"mike",'m',18,"bj"}
fmt.Println("s1 = ", s1)
//指定成员初始化,没有初始化的成员自动复制为0
s2 := Student{name:"mike", addr: "bj"}
fmt.Println("s2 = ", s2)
}
结构体指针变量初始化
func main(){
//顺序初始化,每个成员必须初始化,别忘了&
var p1 *Student = &Student{1,"mike",'m',18,"bj"}
fmt.Println("p1 = ", p1)
//指定成员初始化,没有初始化的成员自动复制为0
p22 := Student{name:"mike", addr: "bj"}
fmt.Println("p2 type is %T\n", p2)
fmt.Println("p2 = ", p2)
}
结构体成员的使用:普通变量
func main(){
//定义一个结构体普通变量
var s Student
//操作成员,需要使用点(.)运算符
s.id = 1
s.name = "mike"
s.sex = "m"
s.addr = "bj"
fmt.Println("s = ", s)
}
结构体成员的使用:指针变量
1、指针有合法指向后,才操作成员
func main(){
//定义一个结构体普通变量
var s Student
//定义一个指针变量,保存s的地址
var p1 *Student
p1 = &s
//通过指针操作成员 p1.id 和(*p1).id完全等价,只能使用.运算符
p1.id = 1
(*p1).name = "mike"
p1.sex = "m"
p1.addr = "bj"
fmt.Println("p1 = ", p1)
}
2、通过new申请一个结构体
p2 := new(Student)
p2.id = 1
p2.name = "mike"
p2.sex = "m"
p2.addr = "bj"
fmt.Println("p2 = ", p2)
结构体比较和赋值
与数组比较相类似,只能用==和!=运算符,返回布尔类型
同类型的结构体可以相互赋值
结构体作为函数参数:值传递
package main
import "fmt"
//定义一个结构体类型
type Student struct{
id int
name string
sex byte //字符类型
age int
addr string
}
func test(s Student){
s.id = 123
fmt.Println("test: ", s)
}
func main(){
s := Student{1, "mike", 'm', 18, "bj"}
test (s) //值传递,形参无法改实参
fmt.Println("main: ", s)
}
结构体作为函数参数:地址传递/引用传递
package main
import "fmt"
//定义一个结构体类型
type Student struct{
id int
name string
sex byte //字符类型
age int
addr string
}
func test(p *Student){
p.id = 123
}
func main(){
s := Student{1, "mike", 'm', 18, "bj"}
test (&s) //地址传递,形参无法改实参
fmt.Println("main: ", s)
}
go语言可见性
如果想使用其他包的函数、结构体类型、结构体成员、函数名、类型名,结构体成员变量名的首字母必须大写。如果首字母是小写,则只能在同一个包里面使用。