首页 文章资讯内容详情

golang-指针,函数,map

2026-06-01 4 花语

本文内容纲要:

-指针 -内置函数 -new函数 -make函数 -函数 -map类型

指针

普通类型变量存的就是值,也叫值类型。指针类型存的是地址,即指针的值是一个变量的地址。

一个指针只是值所保存的位置,不是所有的值都有地址,但是所有的变量都有。使用指针可以在无需知道

变量名字的情况下,间接读取或更新变量的值。

获取变量的地址,用&,例如:varaint获取a的地址:&a,&a(a的地址)这个表达式获取一个指向整型变量的指针,它的类型是整形指针(*int),如果值叫做p,我们说p指向x,或者p包含x的地址,p指向的变量写成

*p,而*p获取变量的值,这个时候*p就是一个变量,所以可以出现在赋值操作符的左边,用于更新变量的值

指针类型的零值是nil

两个指针当且仅当指向同一个变量或者两者都是nil的情况才相等

通过下面小例子进行理解指针:

packagemain import"fmt" funcmain(){ x:=1 //&x获取的是变量x的地址,并赋值给p,这个时候p就是一个指针 //p是指针,所以*p获取的就是变量的值,指针指向的是变量x的值,即*p为1 p:=&x fmt.Println(*p) //这里*p进行赋值,也就是更改了变量x的值,即实现不知道变量的名字更改变量的值 *p=2 fmt.Println(x) }

再看一个关于通过一个函数来修改变量值的问题:

packagemain import"fmt" funcmodify(numint){ num=1000 } funcmain(){ a:=1 modify(a) fmt.Println(a) }

这个例子是修改变量的值,但是最后打印变量a的值是还是10,所以这里就需要知道,当通过定义的函数modify来修改变量的值时,传入变量a其实会进行一次拷贝,传入的其实是a变量的一个副本,所以当通过

modify修改的时候修改的是副本的值,并没有修改变量a的值。

当我们理解指针的之后,就可以通过指针的的方法来解决上面的这个问题,将代码更改为: packagemain import"fmt" funcmodify(num*int){ *num=1000 } funcmain(){ a:=1 modify(&a) fmt.Println(a) }

内置函数

len:用于求长度,比如string、array、slice、map、channel

new:来分配内存,主要来分配值类型,如int、struct。返回的是指针

make:来分配内存,主要来分配引类型,如chan、map、slice

append:来追加元素到数组、slice中

panic和recover:来做错误(这个后续整理)

下面重点整理new和make

new函数

funcnew(Type)*Type

先看一下官网对这个内置函数的介绍:

内置函数new用来分配内存,它的第一个参数是一个类型,不是一个值,它的返回值是一个指向新分配类型零值的指针。这里要特别注意new返回的是一个指针

new函数也是创建变量的一种方式。表达式new(T)创建一个未命名的T类型变量,初始化T类型的零值,并返回其地址(地址类型为*T)

通过下面例子进行理解:

packagemain import"fmt" funcnewFunc(){ p:=new(int) fmt.Println(p)//打印是地址 fmt.Println(*p)//int类型的零值为0这里打印0 *p=2 fmt.Println(*p)//*p已经为其地址指向了一个变量2,所以这里打印为2 } funcmain(){ newFunc() }

这里我们要知道new创建的变量和取其地址的普通局部变量没有什么不同,只是语法上的便利

如果我们定义一个指针是不能直接给这个指针赋值的,而是需要先给这个指针分配内存,然后才能赋值

下面例子先不初始化分配内存,直接赋值会报错,如下: packagemain import"fmt" //new创建变量的方式 funcnewIntFunc()*int{ returnnew(int) } //取地址的普通局部变量 funcnewIntFunc2()*int{ varresint return&res } funcmain(){ varp1*int /** 注意下面这句p1=newIntFunc()必须有,否则会报错 <nil> panic:runtimeerror:invalidmemoryaddressornilpointerdereference [signalSIGSEGV:segmentationviolationcode=0x1addr=0x0pc=0x108d723] goroutine1[running]: main.main() /Users/zhangjinyu/learngo/src/go_dev/day04/new1/new1.go:18+0x63 exitstatus2 */ p1=newIntFunc() fmt.Println(p1) *p1=22 fmt.Println(*p1) varp2*int p2=newIntFunc2() fmt.Println(p2) *p2=33 fmt.Println(*p2) }

make函数

funcmake(Type,sizeIntegerType)Type

先看一下官网对这个内置函数的介绍:

内置函数make用来为slice,map或chan类型分配内存或初始化一个对象(这里需要注意:只能是这三种类型)

第一个参数也是一个类型而不是一个值

返回的是类型的引用而不是指针,而且返回值也依赖具体传入的类型

注意:make返回初始化后的(非零)值。

其实在上一篇整理切片slice的时候就用到了make如:

make([]type,len)

当时通过make来初始化slice的时候,第二个参数指定了它的长度,如果没有第三个参数,它的容量和长度相等,当然也可以传入第三个参数来指定不同的容量值,但是注意不能比长度值小

这里提前说一下通过make初始化map的时候,根据size大小来初始化分配内存,不过分配后的map长度为0,如果size被忽略了,会在初始化分配内存的时候分配一个小的内存

关于new和make的一个小结:

new的作用是初始化一个指向类型的指针(*T),make的作用是为slice,map或者channel初始化,并且返回引用T

函数

函数的声明语法:func函数名(参数表)[(返回值表)]{}

这了要注意第一个花括号必须和func在一行

常见的几种声明函数的方法:

funcadd(){

}

funcadd(aint,bint){

}

funcadd(aint,bint)int{

}

funcadd(aint,bint)(int,int){

}

funcadd(a,bint)(int,int){

}

golang函数的特点:

不支持重载,即一个包不能有两个名字一样的函数

函数也是一种类型,一个函数可以赋值给变量

匿名函数

多返回值

演示一些函数的例子:

packagemain import( "fmt" ) funcadd(a,bint)int{ returna+b } funcmain(){ c:=add//这里把函数名赋值给变量c fmt.Printf("%p%T",c,add) sum:=c(10,20)//调用c其实就是在调用add fmt.Println(sum) }

还有一个:

packagemain import( "fmt" ) typeaddFuncfunc(int,int)int funcadd(a,bint)int{ returna+b } funcoperator(opaddFunc,aint,bint)int{ returnop(a,b) } funcmain(){ c:=add sum:=operator(c,100,200) fmt.Println(sum) }

变量作用域

在函数外面的变量是全局变量

函数内部的变量是局部变量

go中变量的作用域有多种情况:

函数级别的,代码块级别的

关于函数的可变参数

变长函数被调用的时候可以有可变的参数个数

在参数列表最后的类型名称前使用省略号...可以声明一个变长的函数,

例如:

0个或多个参数

funcadd(arg...int)int{

}

1个或多个参数

funcadd(aint,arg...int)int{

}

2个或多个参数

funcadd(aint,bint,arg...int)int{

}

关于函数参数的传递

不管是值类型还是引用传递,传递给函数的都是变量的副本

注意:map,slice,chan,指针,interface默认以引用方式传递

延迟函数defer的调用

语法上,一个defer语句就是一个普通的函数或者方法调用,在调用之前加上关键字defer。函数和参数表达式会在语句执行时求值,但是无论是正常情况还是执行return语句或者函数执行完毕,以及不正常情况下,如程序发生宕机,实际的调用推迟到包含defer语句的函数结束后才执行,defer语句没有限制使用次数。

defer用途:

当函数返回时,执行defer语句,因此可以用来做资源清理 多个defer语句,按先进后出的方式执行 defer语句中的变量,在defer声明时就决定了

先通过一个小例子理解defer:

packagemain import( "fmt" ) functestDefer(){ a:=100 fmt.Printf("beforedefer:a=%d\n",a) deferfmt.Println(a) a=200 fmt.Printf("afterdefer:a=%d\n",a) } funcmain(){ testDefer() }

这里我们可以这样理解当我们执行defer语句的时a=100,这个时候压入到栈中,等程序最后结束的时候才会调用defer语句,所以打印的顺序是最后才打印一个数字100

defer语句经常使用成对的操作,比如打开和关闭,连接和断开,加锁和解锁

下面拿关闭一个打开文件操作为例子,当我们通过os.Open()打开一个文件的时候可以在后面添加deferf.Close()这样在函数结束时就可以帮我们自动关闭一个打开的文件

map类型

key-value的数据结构,又叫字典

声明

varmap1map[keytype]valuetype

例子:

varamap[string]string

varamap[string]int

注意:声明是不会分配内存的需要make初始化

初始化的两种方式:

varmap[string]string=map[string][string]{"hello","world"}

或:

vara=make(map[string]string,10)

插入和更新

a["hello"]="world"

查找

val,ok:=a["hello"]

遍历

fork,v:=rangea{

fmt.println(k,v)

}

删除

delete(a,"hello")

这个操作是安全的,及时这个元素不存在也不会报错,如果一个查找失败将返回value类型对应的零值

长度

len(a)

map是引用类型

注意:map中的元素并不是一个变量,所以我们不能对map的元素进行取址操作

如下:

packagemain import"fmt" funccreateMap(){ /*创建map的四种方式 1)make(map[KeyType]ValueType,initialCapacity) 2)make(map[KeyType]ValueType) 3)map[KeyType]ValueType{} 4)map[KeyType]ValueType{key1:value1,key2:value2,...,keyN:valueN} */ map1:=make(map[string]string,5) map2:=make(map[string]string) map3:=map[string]string{} map4:=map[string]string{"a":"1","b":"2","c":"3","d":"4"} fmt.Println(map1,map2,map3,map4) } //填充和遍历 functraversalMap(){ map1:=make(map[string]string) map1["a"]="1" map1["b"]="2" map1["c"]="3" forindex,val:=rangemap1{ fmt.Printf("%s->%s\n",index,val) } } //更新,查找,删除 funcupdateMap(){ fmt.Println("-----------------------------") map4:=map[string]string{"a":"1","b":"2","c":"3","d":"4"} //查找,返回key对应的value,和是否存在的布尔值 value,exist:=map4["a"] fmt.Printf("%v->%v\n",exist,value) fmt.Println("=============================") //更新 map4["a"]="8" fmt.Printf("%v\n",map4) //删除 delete(map4,"b") fmt.Printf("%v\n",map4) } funcmain(){ createMap() traversalMap() updateMap() }

转自https://www.cnblogs.com/zhaof/p/8129424.html

本文内容总结:指针,内置函数,new函数,make函数,函数,map类型,

原文链接:https://www.cnblogs.com/justdoyou/p/10026004.html