golang-指针,函数,map
指针
普通类型变量存的就是值,也叫值类型。指针类型存的是地址,即指针的值是一个变量的地址。
一个指针只是值所保存的位置,不是所有的值都有地址,但是所有的变量都有。使用指针可以在无需知道
变量名字的情况下,间接读取或更新变量的值。
获取变量的地址,用&,例如:var a int 获取a的地址:&a,&a(a的地址)这个表达式获取一个指向整型变量的指针,它的类型是整形指针(*int),如果值叫做p,我们说p指向x,或者p包含x的地址,p指向的变量写成
*p ,而*p获取变量的值,这个时候*p就是一个变量,所以可以出现在赋值操作符的左边,用于更新变量的值
指针类型的零值是nil
两个指针当且仅当指向同一个变量或者两者都是nil的情况才相等
通过下面小例子进行理解指针:
package main import "fmt" func main() {
x :=
//&x 获取的是变量x的地址,并赋值给p,这个时候p就是一个指针
//p是指针,所以*p获取的就是变量的值,指针指向的是变量x的值,即*p为1
p := &x
fmt.Println(*p)
//这里*p 进行赋值,也就是更改了变量x的值,即实现不知道变量的名字更改变量的值
*p =
fmt.Println(x)
}
再看一个关于通过一个函数来修改变量值的问题:
package main import "fmt" func modify(num int) {
num =
}
func main() {
a :=
modify(a)
fmt.Println(a)
}
这个例子是修改变量的值,但是最后打印变量a的值是还是10,所以这里就需要知道,当通过定义的函数modify来修改变量的值时,传入变量a其实会进行一次拷贝,传入的其实是a变量的一个副本,所以当通过
modify修改的时候修改的是副本的值,并没有修改变量a的值。
当我们理解指针的之后,就可以通过指针的的方法来解决上面的这个问题,将代码更改为:
package main import "fmt" func modify(num *int) {
*num =
}
func main() {
a :=
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函数
func new(Type) *Type
先看一下官网对这个内置函数的介绍:
内置函数 new 用来分配内存,它的第一个参数是一个类型,不是一个值,它的返回值是一个指向新分配类型零值的指针。这里要特别注意new返回的是一个指针
new函数也是创建变量的一种方式。表达式new(T)创建一个未命名的T类型变量,初始化T类型的零值,并返回其地址(地址类型为*T)
通过下面例子进行理解:
package main import "fmt" func newFunc() {
p := new(int)
fmt.Println(p) //打印是地址
fmt.Println(*p) //int类型的零值为0这里打印0
*p =
fmt.Println(*p) //*p已经为其地址指向了一个变量2,所以这里打印为2
} func main() {
newFunc()
}
这里我们要知道new创建的变量和取其地址的普通局部变量没有什么不同,只是语法上的便利
如果我们定义一个指针是不能直接给这个指针赋值的,而是需要先给这个指针分配内存,然后才能赋值
下面例子先不初始化分配内存,直接赋值会报错,如下:
package main import "fmt" //new创建变量的方式
func newIntFunc() *int {
return new(int)
} //取地址的普通局部变量
func newIntFunc2() *int {
var res int
return &res
} func main() {
var p1 *int
/**
注意下面这句p1 = newIntFunc()必须有,否则会报错
<nil>
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x108d723] goroutine 1 [running]:
main.main()
/Users/zhangjinyu/learngo/src/go_dev/day04/new1/new1.go:18 +0x63
exit status 2
*/
p1 = newIntFunc()
fmt.Println(p1)
*p1 =
fmt.Println(*p1) var p2 *int
p2 = newIntFunc2()
fmt.Println(p2)
*p2 =
fmt.Println(*p2)
}
make函数
func make(Type, size IntegerType) 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在一行
常见的几种声明函数的方法:
func add(){
}
func add(a int,b int){
}
func add(a int,b int) int{
}
func add(a int, b int)(int,int){
}
func add(a ,b int)(int,int){
}
golang函数的特点:
- 不支持重载,即一个包不能有两个名字一样的函数
- 函数也是一种类型,一个函数可以赋值给变量
- 匿名函数
- 多返回值
演示一些函数的例子:
package main import (
"fmt"
) func add(a, b int) int {
return a + b
} func main() {
c := add //这里把函数名赋值给变量c
fmt.Printf("%p %T", c, add)
sum := c(, ) //调用c其实就是在调用add
fmt.Println(sum) }
还有一个:
package main import (
"fmt"
) type addFunc func(int, int) int func add(a, b int) int {
return a + b
} func operator(op addFunc, a int, b int) int {
return op(a, b)
} func main() {
c := add
sum := operator(c, , )
fmt.Println(sum)
}
变量作用域
在函数外面的变量是全局变量
函数内部的变量是局部变量
go中变量的作用域有多种情况:
函数级别的,代码块级别的
关于函数的可变参数
变长函数被调用的时候可以有可变的参数个数
在参数列表最后的类型名称前使用省略号...可以声明一个变长的函数,
例如:
0个或多个参数
func add(arg...int) int{
}
1个或多个参数
func add(a int,arg...int) int{
}
2个或多个参数
func add(a int,b int,arg...int)int{
}
关于函数参数的传递
不管是值类型还是引用传递,传递给函数的都是变量的副本
注意:map,slice,chan,指针,interface默认以引用方式传递
延迟函数defer的调用
语法上,一个defer语句就是一个普通的函数或者方法调用,在调用之前加上关键字defer。函数和参数表达式会在语句执行时求值,但是无论是正常情况还是执行return语句或者函数执行完毕,以及不正常情况下,如程序发生宕机,实际的调用推迟到包含defer语句的函数结束后才执行,defer语句没有限制使用次数。
defer用途:
- 当函数返回时,执行defer语句,因此可以用来做资源清理
- 多个defer语句,按先进后出的方式执行
- defer语句中的变量,在defer声明时就决定了
先通过一个小例子理解defer:
package main import (
"fmt"
) func testDefer(){
a :=
fmt.Printf("before defer:a=%d\n",a)
defer fmt.Println(a)
a =
fmt.Printf("after defer:a=%d\n",a) } func main(){
testDefer()
}
这里我们可以这样理解当我们执行defer语句的时a=100,这个时候压入到栈中,等程序最后结束的时候才会调用defer语句,所以打印的顺序是最后才打印一个数字100
defer语句经常使用成对的操作,比如打开和关闭,连接和断开,加锁和解锁
下面拿关闭一个打开文件操作为例子,当我们通过os.Open()打开一个文件的时候可以在后面添加defer f.Close() 这样在函数结束时就可以帮我们自动关闭一个打开的文件
map类型
key-value的数据结构,又叫字典
声明
var map1 map[keytype]valuetype
例子:
var a map[string]string
var a map[string]int
注意:声明是不会分配内存的需要make初始化
初始化的两种方式:
var map[string]string = map[string][string]{"hello","world"}
或:
var a = make(map[string]string,10)
插入和更新
a["hello"] = "world"
查找
val,ok := a["hello"]
遍历
for k,v := range a{
fmt.println(k,v)
}
删除
delete(a,"hello")
这个操作是安全的,及时这个元素不存在也不会报错,如果一个查找失败将返回value类型对应的零值
长度
len(a)
map是引用类型
注意:map中的元素并不是一个变量,所以我们不能对map的元素进行取址操作
如下:
package main import "fmt" func createMap() {
/*创建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, )
map2 := make(map[string]string)
map3 := map[string]string{}
map4 := map[string]string{"a": "", "b": "", "c": "", "d": ""}
fmt.Println(map1, map2, map3, map4)
} //填充和遍历
func traversalMap() {
map1 := make(map[string]string)
map1["a"] = ""
map1["b"] = ""
map1["c"] = ""
for index, val := range map1 {
fmt.Printf("%s->%s\n", index, val)
}
} //更新,查找,删除
func updateMap() {
fmt.Println("-----------------------------")
map4 := map[string]string{"a": "", "b": "", "c": "", "d": ""}
//查找,返回key对应的value,和是否存在的布尔值
value, exist := map4["a"]
fmt.Printf("%v->%v\n", exist, value)
fmt.Println("=============================")
//更新
map4["a"] = ""
fmt.Printf("%v\n", map4)
//删除
delete(map4, "b")
fmt.Printf("%v\n", map4) } func main() { createMap()
traversalMap()
updateMap()
}
转自https://www.cnblogs.com/zhaof/p/8129424.html
golang-指针,函数,map的更多相关文章
- golang指针函数
func main() { a := models.SmsVerify{} a.Id = 100 fmt.Println(a.Id) // 100 test111(a) fmt.Println(a.I ...
- [C++学习笔记14]动态创建对象(定义静态方法实现在map查找具体类名对应的创建函数,并返回函数指针,map真是一个万能类)good
[C++学习笔记14]动态创建对象 C#/Java中的反射机制 动态获取类型信息(方法与属性) 动态创建对象 动态调用对象的方法 动态操作对象的属性 前提:需要给每个类添加元数据 动态创建对象 实 ...
- TODO:Golang指针使用注意事项
TODO:Golang指针使用注意事项 先来看简单的例子1: 输出: 1 1 例子2: 输出: 1 3 例子1是使用值传递,Add方法不会做任何改变:例子2是使用指针传递,会改变地址,从而改变地址. ...
- Golang指针与unsafe
前言 我们知道在golang中是存在指针这个概念的.对于指针很多人有点忌惮(可能是因为之前学习过C语言),因为它会导致很多异常的问题.但是很多人学习之后发现,golang中的指针很简单,没有C那么复杂 ...
- 明显调用的表达式前的括号必须具有(指针)函数类型 编译器错误 C2064
看到“明显调用的表达式前的括号必须具有(指针)函数类型”这句时我才发现我的语文水平有多烂,怎么看都看不懂,折腾了半天才知道是哪里出了问题. 举个简单的例子 class CTest { void (CT ...
- python--函数式编程 (高阶函数(map , reduce ,filter,sorted),匿名函数(lambda))
1.1函数式编程 面向过程编程:我们通过把大段代码拆成函数,通过一层一层的函数,可以把复杂的任务分解成简单的任务,这种一步一步的分解可以称之为面向过程的程序设计.函数就是面向过程的程序设计的基本单元. ...
- C语言回顾-内存管理和指针函数
1.fgets()函数 该函数是一个文件操作相关的函数 暂时使用这个函数可以从键盘上接收一个字符串,保存到数组中 char str[50]; 1)scanf("%s",str);/ ...
- Python 函数式编程 & Python中的高阶函数map reduce filter 和sorted
1. 函数式编程 1)概念 函数式编程是一种编程模型,他将计算机运算看做是数学中函数的计算,并且避免了状态以及变量的概念.wiki 我们知道,对象是面向对象的第一型,那么函数式编程也是一样,函数是函数 ...
- c/c++ 函数指针 指针函数 数组的引用 指针数组 数组指针
1.指针数组数组指针 引用数组 数组的引用 int *a[10] 指针数组 每一个元素都是一个指针 Int (*a)[10] 数组指针 P指向一个含有10个元素的数组 Int (&a)[10] ...
- python 内置函数 map filter reduce lambda
map(函数名,可遍历迭代的对象) # 列组元素全加 10 # map(需要做什么的函数,遍历迭代对象)函数 map()遍历序列得到一个列表,列表的序号和个数和原来一样 l = [2,3,4,5,6, ...
随机推荐
- 四连测Day3
题目链接:https://pan.baidu.com/s/1_vsHfMI_qO-9IDxmFLkHfg 密码: uza8 T1: 小奥的一笔画,判连通性,查奇偶点即可 #include<ios ...
- c++11新特性之future
std::future可以从异步任务中获取结果,一般与std::async配合使用,std::async用于创建异步任务,实际上就是创建一个线程执行相应任务. 先看段代码: #include < ...
- poj 3498 最大流
March of the Penguins Time Limit: 8000MS Memory Limit: 65536K Total Submissions: 4809 Accepted: ...
- PIP 批量更新改为清华这边的镜像更新
之前pip批量更新的时候发现有些包无法更新,而且速度也特别慢,今天尝试了下清华的镜像,速度是真快 # coding=utf-8import pipfrom subprocess import call ...
- Cropper
jQuery.cropper是一款使用简单且功能强大的图片剪裁jQuery插件.该图片剪裁插件支持图片放大缩小,支持图片旋转,支持触摸屏设备,支持canvas,并且支持跨浏览器使用. 官网:https ...
- mysql 添加字段 修改字段为not null
添加一个字段 ALTER TABLE jw_user_role ADD zk_env VARCHAR(16); 修改字段为not null,还要把原来的类型也写出来 ALTER TABLE jw_us ...
- UVA 1640 The Counting Problem
https://vjudge.net/problem/UVA-1640 题意:统计区间[l,r]中0——9的出现次数 数位DP 注意删除前导0 #include<cmath> #inclu ...
- 数据结构:Rope-区间翻转
BZOJ1269 上一篇文章介绍了Rope的简单应用,这里多了一个操作,区间翻转 同时维护一正一反两个rope……反转即交换两个子串 下面给出代码: #include<cstdio> #i ...
- [洛谷P1941] 飞扬的小鸟
洛谷题目链接:飞扬的小鸟 题目描述 Flappy Bird是一款风靡一时的休闲手机游戏.玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙.如果小鸟一不小心撞到了 ...
- Proxmap Sort
这个排序是桶排序和基数排序的改进,理解了前两者,这个排序很容易理解 先回忆下桶排序是怎么回事,它与桶的区别在于入桶规则,桶排序里是1入1号桶,2入2号桶 这个排序把数字分区了,然后给出一个所谓的键,例 ...