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])等。
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