GO语言学习笔记(4)

目录

一、变量作用域

1.局部变量:

2.全局变量

3.形式参数

4.初始化局部和全局变量

二、数组

1.声明数组

2.初始化数组 

3.访问数组元素 

4.多维数组

5.向函数传递数组        

(1).向函数传递数组的简单示例:

(2).为了避免上述的复制开销和直接修改原始数据,会使用切片来代替数组:

三、指针

1.指针的使用

2.空指针

3.指针数组

       (1).代码示例:

4.指向指针的指针

           (1).代码示例:

5.指针作为函数参数

         (1).代码示例:


一、变量作用域

1.局部变量:

        函数内定义的变量称为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量。

package main
 
import "fmt"
 
func main() {
/* 声明局部变量 */
var a, b, c int
 
/* 初始化参数 */
a = 10
b = 20
c = a + b
 
fmt.Printf("结果: a = %d, b = %d and c = %d\n", a, b, c)
}

2.全局变量

        函数外定义的变量称为全局变量,全局变量可以在整个包甚至外部包(被导出后)使用。

package main
 
import "fmt"
 
/* 声明全局变量 */
var g int
 
func main() {
 
/* 声明局部变量 */
var a, b int
 
/* 初始化参数 */
a = 10
b = 20
g = a + b
 
fmt.Printf("结果: a = %d, b = %d and g = %d\n", a, b, g)
}

3.形式参数

        形式参数会作为函数的局部变量来使用。实例如下:

package main
 
import "fmt"
 
/* 声明全局变量 */
//var g int
 
/* 声明全局变量 */
var a int = 20
 
func main() {
 
/* main 函数中声明局部变量 */
var a int = 10
var b int = 20
var c int = 0
 
fmt.Printf("main()函数中 a = %d\n", a)
c = sum(a, b)
fmt.Printf("main()函数中 c = %d\n", c)
}
 
/* 函数定义-两数相加 */
func sum(a, b int) int {
fmt.Printf("sum() 函数中 a = %d\n", a)
fmt.Printf("sum() 函数中 b = %d\n", b)
 
return a + b
}

4.初始化局部和全局变量

        不同类型的局部和全局变量的默认值为:

int为0

float32为0

pointer为nil

二、数组

1.声明数组

        例如以下定义了数组 balance 长度为 10 类型为 float32:

var balance [10] float32

2.初始化数组 

package main
 
import "fmt"
 
func main() {
//1.初始化数组
balance1 := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
fmt.Println(balance1)
//如果数组长度不确定,可以使用 ... 代替数组的长度,编译器会根据元素个数自行推断数组的长度:
balance2 := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
fmt.Println(balance2)
}

3.访问数组元素 

package main
 
import "fmt"
 
func main() {
 
//3.访问列表元素,通过for循环方式遍历
var n [10]int /* n 是一个长度为 10 的数组 */
var i, j int
 
/* 为数组 n 初始化元素 */
for i = 0; i < 10; i++ {
n[i] = i + 100 /* 设置元素为 i + 100 */
}
 
/* 输出每个数组元素的值 */
for j = 0; j < 10; j++ {
fmt.Printf("Element[%d] = %d\n", j, n[j])
}
}

4.多维数组

        多维数组是指数组的元素自身也是数组。这种结构可以用来表示二维矩阵或更高维度的数据结构。最常见的多维数组是二维数组,可以想象成一个表格,有行和列。

package main

import "fmt"

func main() {
    // 创建一个2x3的二维数组
    var twoD [2][3]int
    // 填充数组
    for i := 0; i < 2; i++ {
        for j := 0; j < 3; j++ {
            twoD[i][j] = i + j
        }
    }

    // 打印数组
    fmt.Println("2D array:")
    for _, row := range twoD {
        for _, col := range row {
            fmt.Printf("%d ", col)
        }
        fmt.Println()
    }
}

5.向函数传递数组        

        在Go语言中,当向函数传递数组时,它会按值传递,这意味着函数接收数组参数的时候,实际上是接收的数组的一个副本,而不是原始数组的引用。这可能会导致性能问题,因为数组可能会很大,而拷贝整个数组需要时间和内存。因此,在Go中,更常见的做法是传递一个指向数组的指针,或者使用切片,后者在内部本质上就是一个数组指针的封装。

(1).向函数传递数组的简单示例:

package main

import "fmt"

// 函数接收一个整型数组的指针,这样可以直接修改原始数组
func modifyArray(a *[3]int) {
    (*a)[1] = 10 // 修改数组的第二个元素
}

func main() {
    // 声明并初始化一个整型数组
    arr := [3]int{1, 2, 3}

    // 打印原始数组
    fmt.Println("Before:", arr)

    // 向函数传递数组的地址
    modifyArray(&arr)

    // 打印修改后的数组
    fmt.Println("After:", arr)
}

(2).为了避免上述的复制开销和直接修改原始数据,会使用切片来代替数组:

package main

import "fmt"

// 函数接收一个整型切片,可以直接修改底层的数组
func modifySlice(s []int) {
    s[1] = 10 // 修改切片的第二个元素
}

func main() {
    // 切片是对数组的引用
    slice := []int{1, 2, 3}

    // 打印原始切片
    fmt.Println("Before:", slice)

    // 向函数传递切片
    modifySlice(slice)

    // 打印修改后的切片
    fmt.Println("After:", slice)
}

三、指针

1.指针的使用

        一个指针变量指向了一个值的内存地址。

var ip *int        /* 指向整型*/
var fp *float32    /* 指向浮点型 */
package main
 
import "fmt"
 
func main() {
var a int = 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
 
ip = &a /* 指针变量的存储地址 */
 
fmt.Printf("a 变量的地址是: %x\n", &a)
 
/* 指针变量的存储地址 */
fmt.Printf("ip 变量储存的指针地址: %x\n", ip)
 
/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip)
}

2.空指针

        当一个指针被定义后没有分配到任何变量时,它的值为 nil。

        nil 指针也称为空指针。

        nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。

        一个指针变量通常缩写为 ptr。

package main
 
import "fmt"
 
func main() {
 
//空指针
var ptr *int
fmt.Printf("ptr 的值为 : %x\n", ptr)
}

3.指针数组

        在 Go 语言中,指针数组是一个数组,其每个元素都是指向某种类型值的指针。指针数组可以用来存储数据的地址而非数据本身,这样可以在不同的函数或方法间共享和修改数据,而不需要复制数据本身,这样可以提高效率,尤其是在处理大型结构体或其他需要避免复制的数据类型时。

       (1).代码示例:

package main

import "fmt"

// 定义一个简单的结构体
type Vertex struct {
    X, Y int
}

func main() {
    // 创建一个包含两个元素的 Vertex 结构体数组
    vertices := [2]Vertex{{1, 2}, {3, 4}}

    // 创建一个指针数组,用于存放结构体的指针
    var ptrs [2]*Vertex

    // 将 vertices 数组中每个元素的地址分配给指针数组
    for i := 0; i < len(vertices); i++ {
        ptrs[i] = &vertices[i] // 取地址符 & 获取元素的地址
    }

    // 输出原始数组中的元素
    fmt.Println("Original Vertex array:")
    for _, v := range vertices {
        fmt.Println(v)
    }

    // 修改指针数组指向的结构体字段
    // 这将直接修改原始结构体数组中对应的数据
    ptrs[0].X = 7
    ptrs[1].Y = 8

    // 再次输出原始数组中的元素
    // 可以看到原始数组中的数据已经被更改
    fmt.Println("\nModified Vertex array through pointers:")
    for _, v := range vertices {
        fmt.Println(v)
    }
}

4.指向指针的指针

        在 Go 语言中,你也可以有一个指向指针的指针,即一个二级指针。这是一个存储另一个指针地址的指针。这样的构造在多级间接引用时很有用,比如在链表、树或者是更复杂的数据结构中。

           (1).代码示例:

package main

import "fmt"

func main() {
    a := 100
    p := &a // 指向 a 的指针
    pp := &p // 指向 p 的指针,也就是指向指针的指针

    fmt.Printf("Original value: %d\n", a)
    fmt.Printf("Value via pointer: %d\n", *p)
    fmt.Printf("Value via pointer to pointer: %d\n", **pp)

    **pp = 200 // 通过指向指针的指针更改 a 的值
    fmt.Printf("\nValue after modification via pointer to pointer: %d\n", a)
    fmt.Printf("Value via pointer: %d\n", *p)
    fmt.Printf("Value via pointer to pointer: %d\n", **pp)
}

        在这个示例中,a 是一个整型变量,p 是一个指向 a 的指针,而 pp 是一个指向 p 的指针。

  • %d 是一个格式占位符,用于输出一个整数。
  • &a 获取变量 a 的内存地址。
  • &p 获取指针 p 的内存地址。
  • *p 是间接引用或解引用 p,它给出了 p 指向的变量的值。
  • **pp 是两次解引用 pp,它给出了 pp 指向的指针所指向的变量的值。

        通过二级指针 pp 修改 a 的值是通过两次解引用实现的:首先解引用 pp 来访问 p,然后解引用 p 来访问并修改 a

        输出结果将展示通过直接访问、通过指针以及通过指向指针的指针访问和修改 a 的值。

5.指针作为函数参数

        在Go语言中,将指针作为函数参数传递允许函数直接修改传入的变量的值。这与传值调用不同,在传值调用中,函数接收的参数是原始数据的副本,对这些参数的任何修改都不会影响原始数据。使用指针可以避免数据副本的开销,并且可以修改原始数据。

         (1).代码示例:

package main

import "fmt"

// 定义一个函数,接收一个整型指针作为参数
func doubleValue(num *int) {
    *num *= 2 // 解引用指针并更新它所指向的值
}

func main() {
    value := 10
    fmt.Printf("Original value: %d\n", value)

    // 调用函数时,传入 value 的地址
    doubleValue(&value)

    // value 的值已经被函数 doubleValue 修改
    fmt.Printf("Value after doubleValue function call: %d\n", value)
}

        在这个示例中,doubleValue 函数接受一个指向 int 类型的指针 num 作为参数。在函数内部,我们使用解引用操作符 * 来访问 num 指针指向的值,并将其翻倍。在 main 函数中,我们创建了一个整型变量 value,然后将它的地址传递给 doubleValue 函数。因为我们传递了 value 的地址,doubleValue 函数能够直接修改 value 的原始值。因此,在调用函数后,value 的值从 10 变成了 20。 

 参考资料:

https://wenku.baidu.com/view/20de02adbad528ea81c758f5f61fb7360b4c2bee.html?_wkts_=1707659378577&bdQuery=go%E8%AF%AD%E8%A8%80%E5%90%91%E5%87%BD%E6%95%B0%E4%BC%A0%E9%80%92%E6%95%B0%E7%BB%84&needWelcomeRecommand=1

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大王算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值