liujie
liujie
Published on 2025-03-14 / 10 Visits
0
0

GO 学习(四)

#Go

Go 语言指针

Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。

var a int = 10  

fmt.Printf("变量的地址: %x\n", &a  )

执行以上代码输出结果为:变量的地址: 20818a220

指针

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

类似于变量和常量,在使用指针前你需要声明指针。

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

类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:

var var_name *var-type

var-type 为指针类型,var_name 为指针变量名,* 号用于指定变量是作为一个指针。以下是有效的指针声明:

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 )
}

输出结果为:

a 变量的地址是: 20818a220
ip 变量储存的指针地址: 20818a220
*ip 变量的值: 20

结构体 struct

定义

结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:

type struct_variable_type struct {
   member definition
   member definition
   ...
   member definition
}

变量的声明和结构赋值,语法格式如下
variable_name := structure_variable_type {value1, value2...valuen}
或
variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}

示例:

package main

import "fmt"

type Books struct {
	title   string
	author  string
	subject string
	book_id int
}

func main() {

	// 创建一个新的结构体
	fmt.Println(Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407})

	// 也可以使用 key => value 格式
	fmt.Println(Books{title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", book_id: 6495407})

	// 忽略的字段为 0 或 空
	fmt.Println(Books{title: "Go 语言", author: "www.runoob.com"})

	// 注意:申明时,不像java那样,需要显示的new 实例化,才能赋值。直接申明,然后可以赋值
	var Book1 Books /* 声明 Book1 为 Books 类型 */
	var Book2 Books /* 声明 Book2 为 Books 类型 */

	/* book 1 描述 */
	Book1.title = "Go 语言"
	Book1.author = "www.runoob.com"
	Book1.subject = "Go 语言教程"
	Book1.book_id = 6495407

	/* book 2 描述 */
	Book2.title = "Python 教程"
	Book2.author = "www.runoob.com"
	Book2.subject = "Python 语言教程"
	Book2.book_id = 6495700

	// 按对象的方式打印
	/* 打印 Book1 信息 */
	printBook(Book1)

	/* 打印 Book2 信息 */
	printBook(Book2)

	/* 传变量地址,打印 Book1 信息 */
	pointerPrintBook(&Book1)

	/* 传变量地址,打印 Book2 信息 */
	pointerPrintBook(&Book2)

}

// 对象打印
func printBook(book Books) {
	fmt.Printf("Book title : %s\n", book.title)
	fmt.Printf("Book author : %s\n", book.author)
	fmt.Printf("Book subject : %s\n", book.subject)
	fmt.Printf("Book book_id : %d\n", book.book_id)
}

// 指针类型传参,取出变量地址对应的值
func pointerPrintBook(book *Books) {
	fmt.Printf("Book title : %s\n", book.title)
	fmt.Printf("Book author : %s\n", book.author)
	fmt.Printf("Book subject : %s\n", book.subject)
	fmt.Printf("Book book_id : %d\n", book.book_id)
}

切片 Slice

在 Go 语言中,切片(Slice) 是一个非常重要的数据类型,它类似于 数组,但比数组更加灵活和强大。对于有 Java 开发背景的人来说,切片可以类比为 动态数组 或 ArrayList,但与 Java 中的 ArrayList 或数组有些不同。

1. 定义:

• 切片是一个 动态大小 的、基于数组 的数据结构,它在 Go 中用于处理集合数据。与 Java 中的 ArrayList 类似,切片是可以增长和缩小的,允许对其进行增删操作。

2. 切片的语法:

• 切片的声明方式与数组类似,但它没有固定的长度:

var slice []int  // 声明一个空的切片
slice = append(slice, 1, 2, 3)  // 使用 append() 动态添加元素

3. 和数组的关系:

• 切片背后是由数组支持的。切片本质上是对数组的一个 视图,它包含了指向数组的指针、长度(len)和容量(cap)等信息。

• 与 Java 数组不同,Go 中的数组是 固定大小,一旦创建就不能改变其大小。切片则可以动态调整大小。

4. 切片的特性

动态大小:切片的大小是可以增长或缩小的。比如可以使用 append() 函数动态添加元素。

支持切割操作:可以通过 [:n]、[n:m] 等方式对切片进行切割,得到一个新的切片。

共享底层数组:切片指向底层数组的一部分,当多个切片共享底层数组时,修改一个切片的数据会影响其他切片。

5. 与 Java 的 ArrayList 类比

动态扩展:Java 中的 ArrayList 是一个可以动态增长的数组,容量会根据需要自动增加。Go 中的切片具有类似的特性。切片是基于数组实现的,底层的数组会随着切片的扩展而重新分配内存。

性能:在 Java 中,ArrayList 扩展时需要进行数组复制;在 Go 中,切片的扩展也会触发底层数组的重新分配,但这通常是自动管理的,开发者无需关心。

操作

• Java 中的 ArrayList 具有 add(), remove(), get() 等方法。

• Go 中切片的常用操作是:append()(添加元素)、切割([:n], [n:m])等。

特性

Go 切片 (Slice)

Java ArrayList

底层结构

切片是基于数组的动态视图,指向一个底层数组

ArrayList 是基于数组的动态大小容器,自动扩容

大小

动态大小,支持增删元素

动态大小,自动扩展容量

类型限制

可以包含任何类型的数据,Go 是强类型语言

只能包含相同类型的数据,通常是泛型(<T>

内存管理

切片的容量和长度可动态增长,容量不超过底层数组大小

ArrayList 扩容时会创建新的数组并复制元素

6. 切片的内部结构

Go 切片内部的结构比较简洁,包含以下三个部分:

1. 指针(Pointer):指向底层数组。

2. 长度(Length):切片中当前元素的个数。

3. 容量(Capacity):底层数组的总容量,表示切片最多可以增长到多少个元素。

7. 示例:

package main

import "fmt"

func main() {
    // 创建一个切片
    nums := []int{1, 2, 3}
    fmt.Println("Original slice:", nums) // Output: [1 2 3]

    // 动态添加元素
    nums = append(nums, 4, 5)
    fmt.Println("After append:", nums) // Output: [1 2 3 4 5]

    // 切割切片
    slice1 := nums[1:4]
    fmt.Println("Slice1:", slice1) // Output: [2 3 4]

    // 获取长度和容量
    fmt.Println("Length of nums:", len(nums))  // Output: 5
    fmt.Println("Capacity of nums:", cap(nums)) // Output: 8 (capacity usually doubles when appending)

    // 修改切片内容
    slice1[0] = 10
    fmt.Println("Modified slice1:", slice1)   // Output: [10 3 4]
    fmt.Println("Original nums after modification:", nums) // Output: [1 10 3 4 5]
}

Map(集合)

定义

可以使用内建函数 make 或使用 map 关键字来定义 Map:

/* 使用 make 函数 */
map_variable := make(map[KeyType]ValueType, initialCapacity)


// 创建一个空的 Map
m := make(map[string]int)

// 创建一个初始容量为 10 的 Map
//Go 的 map 使用哈希表(hash table)作为底层实现,哈希表会根据需要自动调整容量。
//当插入元素的数量超过当前容量时,Go 会重新哈希(rehash)并分配更多的内存。这一过程通常是渐进式的,并且通过增大容量来保持高效的查找速度。

m := make(map[string]int, 10)


// 使用字面量创建 Map
m := map[string]int{
    "apple": 1,
    "banana": 2,
    "orange": 3,
}

// 获取键值对
v1 := m["apple"]
v2, ok := m["pear"]  // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值

// 获取 Map 的长度
len := len(m)

// 删除键值对
delete(m, "banana")

范围Range

Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对

for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。格式如下:

for key, value := range oldMap {
    newMap[key] = value
}

如果只想读取 key,格式如下:

for key := range oldMap
或者这样:

for key, _ := range oldMap

如果只想读取 value,格式如下:

for _, value := range oldMap

range也可以用来枚举 Unicode 字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。

    for i, c := range "go" {
        fmt.Println(i, c)
    }
}
//输出
//0 103
//1 111


Comment