Go语言学习(指针与结构体)

指针

指针的概念

Go语言中指针简单易学,使用指针可以更简单的执行一些操作。

变量是一种占位符,底层指向的是一个内存地址。

&取地址符,对于变量a,&a取出这个变量的地址。

指针的使用

  • 定义指针变量

  • 为指针变量赋值 &

  • 指针变量前加上*,来访问指针变量中地址所指向的值

package main

import "fmt"

func main() {
    var a int = 10

    // 声明指针变量,其实就是一个特殊的变量,指向a
    // 定义变量格式 var ptr *类型
    var p *int
    p = &a
    fmt.Printf("a变量的地址:%p\n", &a)   // 0xc00001c0a8
    fmt.Printf("p变量存储的地址:%p\n", p)  // 0xc00001c0a8
    fmt.Printf("p变量自己的地址:%p\n", &p) // 0xc00000a028

    fmt.Println("p变量存储的指针地址指向的值:", *p) // 10
    *p = 20
    fmt.Println(a) // 20 通过指针变量b,改变了a的值
    a = 40
    fmt.Println(*p) // 40

    // 指针的嵌套,指针指向指针,*int就是这个指针对应的类型
    var ptr **int
    ptr = &p
    fmt.Printf("ptr变量存储的指针地址:%p\n", ptr)      // 0xc00000a028
    fmt.Printf("ptr变量自己的地址:%p\n", &ptr)       // 0xc00000a038
    fmt.Printf("*ptr变量存储的指针地址:%p\n", *ptr)    // 0xc00001c0a8
    fmt.Printf("*ptr变量存储的地址指向的值:%d\n", **ptr) // 40
    // 使用指针嵌套,修改变量a的值就有了无数种方式
    **ptr = 50
    fmt.Println(a) // 50
}

数组指针

  • 数组指针:指向数组的指针

  • 指针数组:保存指针的数组

package main

import "fmt"

// 数组指针
func main() {
    // 创建一个数组
    arr1 := [5]int{1, 2, 3, 4, 5}
    fmt.Println(arr1)                   // [1 2 3 4 5]
    fmt.Printf("arr1指向的地址:%p\n", &arr1) // 0xc00000e390

    // 创建一个指针,指向这个数组的地址,通过指针来操作数组
    var p1 *[5]int
    p1 = &arr1
    fmt.Printf("p1指向的地址:%p\n", p1)    // 0xc00000e390
    fmt.Printf("p1自己的地址:%p\n", &p1)   // 0xc00000a030
    fmt.Printf("p1指向的地址的值:%d\n", *p1) // [1 2 3 4 5]

    // 操作数组指针,修改数组
    (*p1)[0] = 100    // 原生写法
    fmt.Println(arr1) // [100 2 3 4 5]
    fmt.Println(*p1)  // [100 2 3 4 5]

    // 语法糖 可以直接用p1来操控数组
    // p1 == arr1
    p1[0] = 100
    fmt.Println(*p1) // [100 2 3 4 5]
    fmt.Println(arr1) // [100 2 3 4 5]
}
package main

import "fmt"

func main() {
    a := 1
    b := 2
    c := 3
    d := 4
    e := 5
    // 创建一个指针数组
    arr1 := [5]*int{&a, &b, &c, &d, &e}
    fmt.Println(arr1) // [0xc000186018 0xc000186030 0xc000186038 0xc000186040 0xc000186048]

    // 通过指针修改a的值
    *arr1[0] = 10
    fmt.Println(a) // 10
}

指针函数

需要是一个函数,这个函数的返回值是一个指针。

package main

import "fmt"

// 指针函数 指针可以用作函数的返回值
func main() {
    p1 := fun()
    fmt.Printf("%p\n", p1)
    fmt.Printf("p1变量的类型:%T\n", p1)        // *[5]int
    fmt.Printf("p1变量的地址:%p\n", &p1)       // 0xc00000a028
    fmt.Printf("p1变量存储的地址指向的值:%d\n", *p1) // [1 2 3 4 5]

    fmt.Println((*p1)[0]) // 1
    p1[0] = 100
    fmt.Println(p1[0]) // 100
}

// 调用该函数,返回的是一个指针
func fun() *[5]int {
    arr := [5]int{1, 2, 3, 4, 5}
    return &arr
}

指针作为函数参数(常用)

package main

import "fmt"

func main() {
    a := 10
    fmt.Println("a变量指向的地址:", &a) // 0xc0000a6058
    fun1(&a)
    fmt.Println(a)               // 20
    fmt.Println("a变量指向的地址:", &a) // 0xc0000a6058
}
func fun1(ptr *int) {
    fmt.Println("ptr 存储的地址:", ptr)      // 0xc0000a6058
    fmt.Println("ptr 自己的地址:", &ptr)     // 0xc0000ca020
    fmt.Println("ptr 存储的地址指向的值:", *ptr) // 10

    *ptr = 20
}

补充:如果没有任何东西在指向变量,变量会被销毁,垃圾回收(GC)。

结构体

变量:定义名字(解决生活中的问题)

判断:分支:true false(解决生活中的问题)

循环:多次操作for(解决生活中的问题)

面向对象编程思维OOP:将世界上的所有物体,抽象成一个一个的类(属性、方法)

用一个变量定义一个人的信息:map[string]string slice

人:属性(name、sex、age)动作(eat() work() sleep())

结构体定义

package main

import "fmt"

// 定义结构体 type 结构体名 struct
type User struct {
    name string
    age  int
    sex  string
}

func main() {
    // 创建结构体对象的方式一
    var user1 User
    // 定义了结构体对象,但不赋值,默认是结构体的零值 {"" 0 ""}
    fmt.Println(user1) // { 0 }
    user1.name = "张三"
    user1.age = 18
    user1.sex = "男"
    fmt.Println(user1) // {张三 18 男}
    // 获取名字
    fmt.Println(user1.name) // 张三

    // 创建结构体对象的方式二
    user2 := User{}
    user2.name = "李四"
    user2.age = 20
    user2.sex = "女"
    fmt.Println(user2) // {李四 20 女}

    // 创建结构体对象的方式三
    user3 := User{
        name: "王五",
        age:  40,
        sex:  "男",
    }
    fmt.Println(user3) // {王五 40 男}

    // 创建结构体对象的方式四(这种不声明属性的方式,赋值时需要参数一一匹配)
    user4 := User{"小明", 25, "男"}
    fmt.Println(user4) // {小明 25 男}
}

结构体指针

package main

import "fmt"

// 定义结构体
type User2 struct {
    name string
    age  int
    sex  string
}

func main() {
    user1 := User2{"张三", 18, "男"}
    fmt.Println(user1) // {张三 18 男}
    // 结构体类型  包.struct名
    fmt.Printf("%T, %p\n", user1, &user1) // main.User2, 0xc00007e4b0

    // 结构体是值类型的
    user2 := user1
    fmt.Println(user2)                    // {张三 18 男}
    fmt.Printf("%T, %p\n", user2, &user2) // main.User2, 0xc00007e540

    user2.name = "李四"
    fmt.Println(user1) // {张三 18 男}
    fmt.Println(user2) // {李四 18 男}
    fmt.Println("=========================")

    // 可以用指针解决值传递的问题
    var user_ptr *User2
    user_ptr = &user1
    // *user_ptr 等价于 user1
    fmt.Println(*user_ptr) // {张三 18 男}
    (*user_ptr).name = "王五"
    fmt.Println(user1) // {王五 18 男}

    // 语法糖
    user_ptr.name = "王五111"
    fmt.Println(user1) // {王五111 18 男}

    // 利用内置函数 new 创建对象。
    // new 关键字创建的对象,返回指针,而不是返回结构体对象
    // 通过这种方式创建的结构体对象更加灵活,突破了结构体值类型的限制
    user3 := new(User2)
    fmt.Println(user3) // &{ 0 }
    (*user3).name = "小明"
    user3.age = 25
    user3.sex = "男"
    fmt.Println(user3) // &{小明 25 男}

    updateUser(user3)
    fmt.Println(user3) // &{小明 20 男}
}
func updateUser(user *User2) {
    user.age = 20
}

匿名结构体

package main

import "fmt"

type Student struct {
    name string
    age  int
}

func main() {
    s1 := Student{"张三", 18}
    fmt.Println(s1) // {张三 18}

    // 匿名结构体 没有名字的结构体,直接可以在函数内部创建出来,创建后就需要赋值使用
    s2 := struct {
        name string
        age  int
    }{
        "李四",
        20,
    }
    fmt.Println(s2.name, s2.age) // 李四 20

    // 匿名字段
    t1 := Teacher{"李四", 25}
    fmt.Println(t1) // {李四 25}
    // 如何打印这个匿名字段,默认使用数据类型当做字段名称
    fmt.Println(t1.string) // 李四
    fmt.Println(t1.int) // 25
}

// 结构体中的匿名字段,没有名字的字段,这时属性类型不能重复
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 = "张三"
    person.age = 18
    person.address = Address{
        city:  "安庆",
        state: "中国",
    }
    fmt.Println(person.name)         // 张三
    fmt.Println(person.age)          // 18
    fmt.Println(person.address)      // {安庆 中国}
    fmt.Println(person.address.city) // 安庆
}

结构体导出

public 公开的,所有地方都可以使用;private 私有的,只能自己使用

如果结构体名称首字母小写,则结构体不能被导出使用。即使结构体成员字段名首字母大写,也不会被导出。

如果结构体名称首字母大写,则结构体可以被导出使用。但只对成员字段首字母大写导出使用,对于首字母小写的成员字段不能被导出使用。

如果存在嵌套结构体,即使嵌套在内层的结构体名称首字母小写,外部也能访问到其中首字母大写的成员字段。

结论:结构体名字、属性名字。如果是大写字母,可以被导出使用;如果是小写字母,不可以被导出使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值