Go语言指针及结构体
指针是一种存储地址的数据类型,引用类型;结构体是一种能够组合各种数据类型的类型,值类型。
指针是存储地址的类型,c/c++里面的指针极度强大,通过对指针的偏移、运算和转换 ,是C/C++ 语言拥有极高性能的根本所在,在操作大块数据和做偏移时即方便又便捷,但是C/C++ 中指针饱受诟病,根本原因是指针的运算和内存释放,经常造成内存溢出等问题。
Go语言也提供了指针,但是不允许指针偏移和运算,这样既安全又保证了操作数据的便利。
一、指针
指针是一种存储地址的数据类型,引用类型
变量、指针和地址三者的关系是,每个变量都拥有地址,指针的值就是地址
声明及初始化
指针声明通过*,取地址通过&,也可以使用new函数获取一个类型的指针(已初始化)
//格式
var pointerName *type
var x int=10
//声明为int型的指针
var num *int
//初始化,取到x的地址
num=&x
//new函数创建指针
var str *string=new(string)
指针的值、指针地址、通过指针访问数据
指针是存储地址的引用类型,所以指针的值就是地址,指针也有自己的地址,指针存储的地址也有对应的数据,可以通过指针访问到。
demo1:通过数组指针来说明
var str [4]string=[4]string{"Hello","Good","Yes","Nice"}
//数组指针
var p =&str
fmt.Println("指针中存储的是地址:",p)
fmt.Println("指针本身的地址:",&p)
fmt.Println("指针中存储的地址的值:",*p)
fmt.Println("数组指针访问数组,通过下标",p[0])
//修改数据
p[1]="Bad"
fmt.Println("指针是引用类型,所以原数据也被修改:",*p)
指针中存储的是地址: &[Hello Good Yes Nice]
指针本身的地址: 0xc00008c018
指针中存储的地址的值: [Hello Good Yes Nice]
数组指针访问数组,通过下标 Hello
指针是引用类型,所以原数据也被修改: [Hello Bad Yes Nice]
**demo2:**还是数组指针
s:=[5] int{1,3,1,4,77}
//ptr:=&s
var ptr *[5]int
ptr=&s
fmt.Printf("数组的类型%T\n",s)
fmt.Printf("指针的类型%T\n",ptr)
for _,v:= range ptr{
fmt.Printf("数据为:%d\n",v)
}
i:= new([5] string)
fmt.Printf("类型是%T\n",i)
l:=[5]string{"Hello","yes","good","nice","well"}
i=&l
fmt.Printf("类型是%T\n",i)
数组的类型[5]int
指针的类型*[5]int
数据为:1
数据为:3
数据为:1
数据为:4
数据为:77
类型是*[5]string
类型是*[5]string
demo3:通过结构体指针来说明,和以上一样,值得一提的的是访问结构体数据,可以省略*。
关于结构体,见下文。
func main(){
booKs := BooKs{"《西游记》", "吴承恩"}
ptr := new(BooKs)
ptr=&booKs
//结构体指针访问数据,两种都可以
fmt.Println((*ptr).name,ptr.author)
}
type BooKs struct {
name string
author string
}
《西游记》 吴承恩
数组指针、指针数组
数组指针是数组类型的指针
var apt [6]int//int型的数组
var aptr *[6] int//int型数组指针
var aptr *[6] int
var arr [6] int=[6] int{1,2,3,4,5,6}
aptr=&arr
fmt.Println(*aptr)
//[1 2 3 4 5 6]
指针数组是数组,其中的数据类型是地址
a:="hello"
b:="yes"
c:="world"
var parr [3]*string=[3]*string {&a,&b,&c}
fmt.Println("指针数组中存储的是地址:",parr)
fmt.Println("通过下标来访问数据:",parr[0])
fmt.Println("取得地址所对应的数据",*parr[0])
//*parr非法,因为
指针数组中存储的是地址: [0xc0000301f0 0xc000030200 0xc000030210]
通过下标来访问数据: 0xc0000301f0
取得地址所对应的数据 hello
二、结构体
结构体是类型中带有成员的复合类型,值类型。
Go 语言不是面向对象的语言,但可以使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性。
结构体(struct)和其他语言的类(class)有同等的地位,但Go语言放弃了包括继承在内的大量面向对象特性,只保留了组合(composition)这个最基础的特性。
结构体成员是由一系列的成员变量构成,这些成员变量也被称为“字段”。字段有以下特性:字段拥有自己的类型和值。字段名必须唯一。字段的类型也可以是结构体,甚至是字段所在结构体的类型。
type关键字
关键字 type 可以将各种基本类型定义为自定义类型,结构体、接口等使用type关键字来声明。
结构体的定义格式如下:
type 类型名 struct {
字段1 字段1类型
字段2 字段2类型
…
}
对各个部分的说明:
- 类型名:标识自定义结构体的名称,在同一个包内不能重复。
- struct{}:表示结构体类型,type 类型名 struct{} 可以理解为将 struct{} 结构体定义为类型名的类型。
- 字段1、字段2……:表示结构体字段名。结构体中的字段名必须唯一。
- 字段1类型、字段2类型……:表示结构体字段的类型。
如下:
type Person struct {
name string
age int
gender string
}
结构体中还可以嵌套结构体:
type Person struct {
name string
age int
gender string
}
type Book struct {
name string
anthor string
}
type Read struct {
person Person
book Book
}
声明及初始化
结构体初始化有多种方式
//初始化,不带字段
var p=Person{"Tom",22,"female"}
//初始化,带字段
var p1=Person{name:"Tom",age:22,gender:"female"}
//只做声明
var p2 Person
//赋值
p2.name="Jack"
p2.age=19
p2.gender="male"
fmt.Println("P:",p)
fmt.Println("P1:",p1)
fmt.Println("P2:",p2)
//嵌套结构体的初始化
var read=Read{p,Book{"《西游记》","吴承恩"}}
fmt.Printf("%v\n",read)
//访问嵌套的数据
fmt.Println(read.book)
fmt.Println(read.book.name)
}
type Person struct {
name string
age int
gender string
}
type Book struct {
name string
anthor string
}
type Read struct {
person Person
book Book
}
结果如下:
P: {Tom 22 female}
P1: {Tom 22 female}
P2: {Jack 19 male}
{{Tom 22 female} {《西游记》 吴承恩}}
{《西游记》 吴承恩}
《西游记》
new关键字初始化
new关键字是用来初始化的,返回的是指向类型的指针,关于new的用法在下一篇也有讲解,在此只需要知道是用来返回指针即可。
new关键字的用法是
v:=new(type)
用new关键字创建的都是指针,创建结构体就是结构体指针。如下:
func main() {
p:=Persons{"Sally",18,"female"}
//new关键字得到的是结构体类型的指针
p1:=new(Persons)
//通过取地址符拿到数据
p1=&Persons{"Jim",10,"male"}
fmt.Printf("p的类型是%T\n",p)
fmt.Printf("p1的类型是%T\n",p1)
fmt.Printf("p的名字是%s,年龄为%d\n",p.name,p.age)
//两种取数据方式都可以
fmt.Printf("p1的名字是%s,年龄为%d\n",(*p1).name,p1.age)
}
type Persons struct {
name string
age int
gender string
}
p的类型是main.Persons
p1的类型是*main.Persons
p的名字是Sally,年龄为18
p1的名字是Jim,年龄为10
p1是结构体类型的指针,p是结构体。
指针和结构体非常强大,指针可以用来操作地址,结构体则可以完成各种类型的组合,还可以模拟面向对象的各种特性。