Go语言基础之指针与结构体
指针与结构体
指针
Go 语言中指针是很容易学习的,Go 语言中使用指针可以更简单的执行一些任务。
变量是一种占位符,底层指向是一个内存地址。
&取地址符,拿到一个变量 a,&a取出这个变量的地址。
指针的概念
&取地址符
b内存地址 0x11111111111, 值:500 var b int = 500
a指针变量,指向了一个内存地址 0x11111111111
变量a持有了变量b的地址。这个就是a指向了b
package main
import "fmt"
// 指针
func main() {
var a int = 10
// b存储的是a的地址,b也有自己的地址
var b = &a
fmt.Println("a变量的地址:", &a) // 0xc00000e0a8
fmt.Println("b存储的地址:", b) // 0xc00000e0a8-->10
fmt.Println("b自己的地址:", &b) // 0xc00000a028
// & *
// b 指向的地址 , * 取出 b变量所指向的地址 中的值。
fmt.Println("b变量指向的地址中的值:", *b)
*b = 20
fmt.Println(a) // a 变量20,通过指针变量b,操控了a的值。
}
指针如何使用
package main
import "fmt"
// 指针的使用
/*
1、定义指针变量
2、为指针变量赋值 &
3、访问指针变量中地址所指向的值 *
* :在指针类型前面加上 * 号,就是来获取指针所指向的地址的值
*/
func main() {
// 声明 普通变量
var a int = 10
fmt.Printf("a 变量的值:%d\n", a) // 10
fmt.Printf("a 变量的地址:%p\n", &a) // 0x
// 声明 指针变量,指向a, 指针其实就是一个特殊的变量而已。,ptr命名 p
// 定义变量格式 var ptr *类型
var p *int
p = &a // 指针变量赋值
fmt.Printf("p 变量存储的指针地址:%p\n", p) // a 的地址
fmt.Printf("p 变量自己的地址:%p\n", &p) // p 变量自己的地址
fmt.Printf("p 变量存储的指针地址指向的值%d\n", *p) // a的值
// 改变a的值 *p 和 a 其实是同一个地址
a = 20
fmt.Printf("a:%d\n", a)
fmt.Printf("*p的值:%d\n", *p)
// 通过指针改变a的值
*p = 40
fmt.Printf("a:%d\n", a)
fmt.Printf("*p的值:%d\n", *p)
// 指针的套娃,指针指向指针 , 指针类型 第一个*指针类型, *int是这个指针对应的类型
// 如何理解多个符号,第一个取出来后,后面就是它的类型 *(*(int))
var ptr **int
ptr = &p
//
fmt.Printf("ptr变量存储的指针的地址:%p\n", ptr)
fmt.Printf("ptr变量自己的地址:%p\n", &ptr)
fmt.Printf("*ptr变量存储的地址:%p\n", *ptr)
fmt.Printf("*ptr变量存储的地址中的值:%d\n", **ptr)
// 修改变量a就有了无数种方式
**ptr = 1111
fmt.Println(a)
}
数组指针
- 数组指针,首先应该是一个指针,指向了一个数组
package main
import "fmt"
// 数组指针
func main() {
// 创建数组,值传递。fun
arr1 := [4]int{1, 2, 3, 4}
fmt.Println("arr1:", arr1)
fmt.Printf("arr1指向的地址:%p\n", &arr1)
// 创建一个指针,指向这个数组的地址,通过指针来操作数组
var p1 *[4]int
p1 = &arr1
fmt.Printf("p1指向的地址: %p\n", p1)
fmt.Printf("p1自己的地址: %p\n", &p1)
fmt.Println("p1指向的地址的值: ", *p1)
// 操作数组指针 来修改数组
(*p1)[0] = 100 // 原生写法
fmt.Println("arr1:", arr1)
fmt.Println("p1指向的地址的值: ", *p1)
// 语法糖:由于p1指向了arr1这个数组,所以可以直接用p1来操控数组
// 指向了谁,这个指针就可以代表谁。
// p1 = arr1
p1[0] = 200 // 在程序中,我们更多时候是这样在使用指针的
fmt.Println("arr1:", arr1)
fmt.Println("p1指向的地址的值: ", *p1)
}
- 指针数组,首先应该是一个数组,保存是指针
package main
import "fmt"
func main() {
a := 1
b := 2
c := 3
d := 4
// 创建一个指针数组
arr1 := [4]*int{&a, &b, &c, &d}
fmt.Println(arr1)
// 通过指针修改a的值
// arr1[0] 0xc00000e0a8
*arr1[0] = 100
fmt.Println(a)
a = 200
fmt.Println(*arr1[0])
}
指针函数
首先需要是一个函数,这个函数的返回值是一个指针。
package main
import "fmt"
// 指针函数, 指针是可以用作函数的返回值
func main() {
// 调用了这个函数后,可以得到一个指针类型的变量。
ptr := f5()
fmt.Println("ptr:", ptr)
fmt.Printf("ptr类型:%T\n", ptr)
fmt.Println("ptr的地址:\n", &ptr)
fmt.Println("ptr地址中的值:\n", *ptr)
// 使用
fmt.Println((*ptr)[0])
ptr[0] = 10
fmt.Println(ptr[0])
}
// 调用该函数后返回一个指针
func f5() *[4]int {
arr := [4]int{1, 2, 3, 4}
return &arr
}
指针作为函数参数
package main
import "fmt"
func main() {
// 值传递
a := 10
fmt.Println("a:", a) // 10
fmt.Println("a addr:", &a) // 0xc00000e0a8
f6(&a)
fmt.Println("a:", a) // 20
fmt.Println("a addr:", &a) // 0xc00000e0a8
}
// 指针当做函数的参数
func f6(ptr *int) {
fmt.Println("ptr:", ptr) // 0xc00000e0a8
fmt.Println("ptr 指针的地址中的值", *ptr) // a = 10
*ptr = 20
}
补充:什么时候定义的变量会销毁,没有任何东西再指向它的时候,垃圾回收(GC)会回收。
结构体
变量:解决生活中问题(定义名字)
判断:解决生活中问题(分支:true false)
循环:解决生活中问题(循环:多次操作 for)
面向对象编程思维 OOP:将世界上的所有东西,抽象成一个个的类(属性、方法)
我想用一个变量来定义一个人的信息: map[string]string slice(,)
人:属性(name、sex、age) 动作( 吃 eat() 工作 work() sleep() )
定义一个结构体
定义变量 var
定义结构体 type User struct {}
package main
import "fmt"
// 定一个结构体 type User struct
type User struct {
name string
age int
sex string
}
func main() {
// 通过结构体创建对象, 以前的类型都是用的基本类型,自己定义类型了type,Struct
// 定义了结构体对象,不赋值,默认都是这个结构体的零值 {"",0,""}
var user1 User
fmt.Println("user:", user1)
// 给结构体对象赋值。 xxx.属性 = 值
user1.name = "张三"
user1.age = 1
user1.sex = "女"
fmt.Println("user:", user1)
// 获取这个人的名字
fmt.Println("user:", user1.name)
// 创建对象的方式二
user2 := User{}
user2.name = "qinjiang"
user2.age = 27
user2.sex = "男"
fmt.Println("user:", user2)
fmt.Println("user:", user2.name)
// // 创建对象的方式三
user3 := User{
name: "feige",
age: 35,
sex: "男",
}
fmt.Println("user:", user3)
fmt.Println("user:", user3.name)
// 创建对象的方式四,这种不声明属性的方式,需要参数一一匹配
user4 := User{"guoguo", 18, "女"}
fmt.Println("user:", user4)
}
结构体指针
package main
import "fmt"
// 定一个结构体 type User struct
type User2 struct {
name string
age int
sex string
}
func main() {
// 结构体类型 包.struct名
user1 := User2{"kuangshen", 18, "男"}
fmt.Println(user1)
fmt.Printf("%T,%p\n", user1, &user1) // main.User2,0xc00007e4b0
// 结构体是值类型的
user2 := user1
fmt.Println(user2)
fmt.Printf("%T,%p\n", user2, &user2) // main.User2,0xc00007e540
user2.name = "tywin"
fmt.Println(user1)
fmt.Println(user2)
fmt.Println("========================")
// 指针解决值传递的问题
var user_ptr *User2
user_ptr = &user1
// *user_ptr 等价于 user1
fmt.Println(*user_ptr)
(*user_ptr).name = "qinjiang"
fmt.Println(user1)
// 语法糖
user_ptr.name = "qinjiang222222222"
fmt.Println(user1)
// 内置函数 new 创建对象。 new 关键字创建的对象,都返回指针,而不是结构体对象。
// func new(Type) *Type
// 通过这种方式创建的结构体对象更加灵活,突破了结构体是值类型的限制。
user3 := new(User2)
fmt.Println(user3)
(*user3).name = "小红"
user3.sex = "女"
user3.age = 18
fmt.Println(user3)
updateUser(user3)
fmt.Println(user3)
}
func updateUser(user *User2) {
user.age = 100
}
匿名结构体
package main
import "fmt"
// 匿名结构体:没有名字的结构体
type Student struct {
name string
age int
}
func main() {
s1 := Student{"kuangshen", 18}
fmt.Println(s1.name, s1.age)
// 匿名结构体,直接可以在函数内部创建出来,创建后就需要赋值使用
s2 := struct {
name string
age int
}{
name: "张三",
age: 3,
}
fmt.Println(s2.name, s2.age)
// 匿名字段
t1 := Teacher{"qinjiang", 18}
fmt.Println(t1)
// 如何打印这个匿名字段,默认使用数据类型当做字段名称
fmt.Println(t1.string)
fmt.Println(t1.int)
}
// 结构体中的匿名字段,没有名字的字段,这个时候属性类型不能重复
type Teacher struct {
string
int
}
结构体嵌套
复杂对象构建,可以这么定义
package main
import "fmt"
// 一个结构体可能包含一个字段,而这个字段又是一个结构体:结构体嵌套
type Person struct {
name string
age int
address Address
}
type Address struct {
city, state string
}
// 结构体是可以嵌套:我们就可以定义很多复杂的对象,来进行拼接了,构成一个更大的对象
func main() {
var person = Person{}
person.name = "kuangshen"
person.age = 18
person.address = Address{
city: "广州",
state: "中国",
}
fmt.Println(person.name)
fmt.Println(person.age)
fmt.Println(person.address)
fmt.Println(person.address.city)
}
结构体导出
public 公开的,所有地方都可以使用 、priavte私有的,只能自己使用
结构体:结构体名字,属性名字 。大写字母,可以导出使用,小写字母,不能导出使用。
如果结构体名称首字母小写,则结构体不会被导出。这时,即使结构体成员字段名首字母大写,也不会被导出。
如果结构体名称首字母大写,则结构体可被导出,但只会导出大写首字母的成员字段,那些小写首字母的成员字段不会被导出。
如果存在嵌套结构体,即使嵌套在内层的结构体名称首字母小写,外部也能访问到其中首字母大写的成员字段。
将我讲的所有demo自己手动写一遍,并理解
预习 面向对象
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果