(7-3)结构体和共用体:使用结构体

7.3  使用结构体

在本章前面讲解了结构体的定义和初始化知识,也讲解了使用结构体成员的知识。在本节的内容中,将进一步讲解在Go程序中使用结构体的知识。

7.3.1  将结构体作为函数的参数

在 Go 语言中,结构体是一种自定义数据类型。它允许您将多个不同的变量组合成一个单一的对象,并且可以作为其他函数的参数传递。在将结构体传递给函数时,可以使用值传递或指针传递。当使用值传递时,函数将接收到结构体的副本,而不是原始结构体本身。这意味着,在函数内部对结构体进行任何更改都不会影响到原始结构体。例如在下面的代码中,调用函数CalculateArea()来计算矩形的面积。

type Rectangle struct {
    Width  float64
    Height float64
}

func CalculateArea(rect Rectangle) float64 {
    rect.Width = 20.0 // 修改结构体副本
    return rect.Width * rect.Height
}

func main() {
    r := Rectangle{Width: 10.0, Height: 20.0}
    area := CalculateArea(r)
    fmt.Println("Area:", area) // 输出 Area: 200
    fmt.Println("Width:", r.Width) // 输出 Width: 10
}

在上述代码中,函数CalculateArea()将结构体副本的宽度修改为 20,而没有更改原始结构体。因此输出结果表明矩形的面积为 200,但宽度仍然为 10。

相反,如果想要在函数CalculateArea()内部修改原始结构体,请使用指针。指针传递允许函数直接访问原始结构体的内存地址。例如下面的演示代码:

func CalculateArea(rect *Rectangle) float64 {
    rect.Width = 20.0 // 修改原始结构体
    return rect.Width * rect.Height
}

func main() {
    r := Rectangle{Width: 10.0, Height: 20.0}
    area := CalculateArea(&r)
    fmt.Println("Area:", area) // 输出 Area: 400
    fmt.Println("Width:", r.Width) // 输出 Width: 20
}

在上述代码中,使用指针将结构体传递给 CalculateArea 函数。在该函数中,我们直接访问了原始结构体的内存地址,并修改了其宽度属性。因此,输出结果表明矩形的面积为 400,宽度也已经被修改为 20。

注意:在使用指针时必须小心,确保不会访问未分配内存或空指针,否则可能导致程序崩溃或出错。

实例7-5:图书展示系统(源码路径:Go-codes\7\can.go

实例文件can.go的具体实现代码如下所示。

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

func main() {
	var Book1 Books        /* 声明 Book1 为 Books 类型 */
	var Book2 Books        /* 声明 Book2 为 Books 类型 */

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

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

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

	/* 打印 Book2 信息 */
	printBook(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)
}

执行后会输出:

Book title : Go 语言
Book author : www.toppr.net
Book subject : Go 语言教程
Book book_id : 6495407
Book title : Python 教程
Book author : www.toppr.net
Book subject : Python 语言教程
Book book_id : 6495700

7.3.2  结构体数组

在 Go 语言中,结构体数组是一种将多个结构体对象存储在单个数组中的方法。例如可以使用以下代码定义一个结构体数组:

type Person struct {
    Name string
    Age  int
}


var people [3]Person

在上述代码中,首先定义了一个名为 Person 的结构体类型,其中包含一个名为 Name 的字符串和一个名为 Age 的整数。接下来,我们定义了一个名为 people 的长度为 3 的 Person 类型数组。

接下来,我们可以使用以下代码初始化结构体数组:

people := [3]Person{
    {Name: "Alice", Age: 25},
    {Name: "Bob", Age: 30},
    {Name: "Charlie", Age: 35},
}

在上述代码中,使用结构体字面量的方式初始化了一个名为 people 的 Person 类型数组。每个元素都是一个 Person 类型的结构体,并且包含名字和年龄属性。

访问结构体数组的元素与访问其他类型数组的元素相同。例如在下面的代码中,将输出第二个人的姓名和年龄:

fmt.Println("Name:", people[1].Name) // 输出 Name: Bob

fmt.Println("Age:", people[1].Age) // 输出 Age: 30

由此可见,结构体数组在处理大量相似数据时非常有用,它使得可以轻松地按索引访问结构体对象,并对它们进行排序、过滤和其他常见操作。

实例7-6:排序输出学生信息(源码路径:Go-codes\7\sort.go

在本实例中,使用结构体数组来表示一组学生,然后对这些学生进行排序并输出他们的姓名和年龄。实例文件sort.go的具体实现代码如下所示。

import (
	"fmt"
	"sort"
)

type Student struct {
	Name string
	Age  int
}

func main() {
	students := []Student{
		{Name: "Alice", Age: 20},
		{Name: "Bob", Age: 18},
		{Name: "Charlie", Age: 19},
	}

	fmt.Println("Before sorting:")
	for _, student := range students {
		fmt.Println(student.Name, student.Age)
	}

	sort.Slice(students, func(i, j int) bool {
		return students[i].Age < students[j].Age
	})

	fmt.Println("\nAfter sorting:")
	for _, student := range students {
		fmt.Println(student.Name, student.Age)
	}
}

在上述代码中,首先定义了一个名为 Student 的结构体类型,其中包含一个名为 Name 的字符串属性和一个名为 Age 的整数属性。接下来,我们创建了一个名为 students 的 Student 类型切片,其中包含三个学生的姓名和年龄。然后,使用函数sort.Slice()对学生按照年龄进行排序,并输出排序后的结果。执行后会输出:

Before sorting:
Alice 20
Bob 18
Charlie 19

After sorting:
Bob 18
Charlie 19
Alice 20

7.3.3  结构体函数

在 Go 语言中,结构体函数是一种特殊类型的函数,它与结构体类型相关联。结构体函数是 Go 语言中一种强大的特性,它使得可以将行为和数据结合在一起,并更好地封装代码。结构体函数使用接收器(或称为方法接收器)来将结构体类型绑定到函数。

例如在下面的代码中,首先定义了一个名为 Rectangle 的结构体类型,并包含两个浮点数属性,分别表示矩形的宽度和高度。

type Rectangle struct {
    Width  float64
    Height float64
}



func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

接下来,我们定义了函数Area(),并将其绑定到 Rectangle 类型上。在函数Area()中,我们使用接收器 r 来访问结构体的属性,并计算矩形的面积。接下来可以使用以下方式调用 Area 函数:

r := Rectangle{Width: 10.0, Height: 20.0}
area := r.Area()
fmt.Println("Area:", area)

在上述代码中,创建了一个名为 r 的 Rectangle 类型变量,并设置其宽度为 10,高度为 20。然后,我们调用 Area 函数来计算矩形的面积,并将结果存储在变量 area 中。最后,输出变量 area 的值。

注意:当函数绑定到结构体类型时,接收器可以是值类型或指针类型。如果接收器是值类型,函数将使用结构体的副本,而不是原始结构体本身。如果接收器是指针类型,则函数将使用原始结构体,并且对其进行修改也会影响原始结构体。此外,绑定到结构体类型的函数可以具有任何可选参数和返回值。它们与常规函数的定义方式相同,只是添加了接收器参数。

实例7-7:扑克的洗牌和抽牌(源码路径:Go-codes\7\puke.go

本实例演示了如何使用结构体函数来实现扑克牌的操作,包括洗牌、抽牌等功能。它可以帮助您更好地理解 Go 语言中结构体函数的实用性。实例文件puke.go的具体实现代码如下所示。

import (
	"fmt"
	"math/rand"
	"time"
)

type Card struct {
	Rank string
	Suit string
}

type Deck []Card

func (d Deck) Shuffle() {
	rand.Seed(time.Now().UnixNano())
	for i := range d {
		j := rand.Intn(i + 1)
		d[i], d[j] = d[j], d[i]
	}
}

func (d Deck) Draw() (Deck, Card) {
	if len(d) == 0 {
		return nil, Card{}
	}
	return d[1:], d[0]
}

func NewDeck() Deck {
	ranks := []string{"Ace", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"}
	suits := []string{"Clubs", "Diamonds", "Hearts", "Spades"}

	deck := make(Deck, 0, len(ranks)*len(suits))

	for _, suit := range suits {
		for _, rank := range ranks {
			card := Card{Rank: rank, Suit: suit}
			deck = append(deck, card)
		}
	}

	return deck
}

func main() {
	deck := NewDeck()
	fmt.Println("New deck:", deck)

	deck.Shuffle()
	fmt.Println("\nShuffled deck:", deck)

	hand, drawnCard := deck.Draw()
	fmt.Println("\nDrawn card:", drawnCard)
	fmt.Println("Remaining cards:", hand)
}

对上述代码的具体说明如下:

  1. 首先定义了一个名为 Card 的结构体类型,表示扑克牌中的一张卡片。
  2. 然后,定义了一个名为 Deck 的自定义类型,表示包含多张卡片的扑克牌组合。
  3. 接下来,定义了三个函数作为 Deck 类型的结构体函数:Shuffle()、Draw() 和 NewDeck()。其中,函数Shuffle()使用 Fisher-Yates 算法对扑克牌进行洗牌;函数Draw()从牌堆中抽取一张卡片,并返回更新后的牌堆和所抽取的卡片;函数NewDeck()创建一个新的扑克牌组合,并返回一个 Deck 类型切片。
  4. 在主函数main()中,使用函数NewDeck()创建一个新的扑克牌组合,并输出其内容。然后,我们调用函数Shuffle()对扑克牌进行洗牌,并将结果输出到控制台。最后,我们调用函数Draw()从洗好的牌堆中抽取一张卡片,并输出结果。

执行后会输出:

New deck: [{Ace Clubs} {1 Clubs} {2 Clubs} {3 Clubs} {4 Clubs} {5 Clubs} {6 Clubs} {7 Clubs} {8 Clubs} {9 Clubs} {10 Clubs} {Jack Clubs} {Queen Clubs} {King Clubs} {Ace Diamonds} {1 Diamonds} {2 Diamonds} {3 Diamonds} {4 Diamonds} {5 Diamonds} {6 Diamonds} {7 Diamonds} {8 Diamonds} {9 Diamonds} {10 Diamonds} {Jack Diamonds} {Queen Diamonds} {King Diamonds} {Ace Hearts} {1 Hearts} {2 Hearts} {3 Hearts} {4 Hearts} {5 Hearts} {6 Hearts} {7 Hearts} {8 Hearts} {9 Hearts} {10 Hearts} {Jack Hearts} {Queen Hearts} {King Hearts} {Ace Spades} {1 Spades} {2 Spades} {3 Spades} {4 Spades} {5 Spades} {6 Spades} {7 Spades} {8 Spades} {9 Spades} {10 Spades} {Jack Spades} {Queen Spades} {King Spades}]



Shuffled deck: [{1 Hearts} {King Spades} {5 Clubs} {10 Clubs} {Ace Hearts} {2 Hearts} {6 Diamonds} {Jack Diamonds} {5 Spades} {Jack Clubs} {10 Hearts} {Jack Spades} {8 Clubs} {7 Hearts} {2 Diamonds} {King Hearts} {3 Clubs} {8 Spades} {1 Diamonds} {5 Hearts} {1 Clubs} {10 Diamonds} {Queen Diamonds} {6 Clubs} {2 Clubs} {4 Hearts} {Ace Diamonds} {8 Diamonds} {Queen Hearts} {4 Spades} {Queen Spades} {5 Diamonds} {9 Diamonds} {Ace Spades} {King Clubs} {1 Spades} {3 Diamonds} {Jack Hearts} {3 Spades} {6 Spades} {7 Spades} {2 Spades} {8 Hearts} {10 Spades} {9 Spades} {9 Hearts} {6 Hearts} {4 Diamonds} {7 Diamonds} {3 Hearts} {7 Clubs} {4 Clubs} {Queen Clubs} {Ace Clubs} {9 Clubs} {King Diamonds}]



Drawn card: {1 Hearts}

Remaining cards: [{King Spades} {5 Clubs} {10 Clubs} {Ace Hearts} {2 Hearts} {6 Diamonds} {Jack Diamonds} {5 Spades} {Jack Clubs} {10 Hearts} {Jack Spades} {8 Clubs} {7 Hearts} {2 Diamonds} {King Hearts} {3 Clubs} {8 Spades} {1 Diamonds} {5 Hearts} {1 Clubs} {10 Diamonds} {Queen Diamonds} {6 Clubs} {2 Clubs} {4 Hearts} {Ace Diamonds} {8 Diamonds} {Queen Hearts} {4 Spades} {Queen Spades} {5 Diamonds} {9 Diamonds} {Ace Spades} {King Clubs} {1 Spades} {3 Diamonds} {Jack Hearts} {3 Spades} {6 Spades} {7 Spades} {2 Spades} {8 Hearts} {10 Spades} {9 Spades} {9 Hearts} {6 Hearts} {4 Diamonds} {7 Diamonds} {3 Hearts} {7 Clubs} {4 Clubs} {Queen Clubs} {Ace Clubs} {9 Clubs} {King Diamonds}]

7.3.4  结构体指针

在 Go 语言中,结构体指针是一种特殊类型的指针,它指向一个结构体变量的内存地址。使用结构体指针可以避免复制结构体变量,并在函数间传递大型结构体时提高效率。例如在下面的代码中,首先定义了一个名为 Person 的结构体类型,其中包含一个名为 Name 的字符串和一个名为 Age 的整数。

type Person struct {
    Name string
    Age  int
}

func main() {
    p1 := &Person{Name: "Alice", Age: 25}
    fmt.Println("Before:", *p1)

    updatePerson(p1)
    fmt.Println("After:", *p1)
}

func updatePerson(p *Person) {
    p.Age = 30
}

接下来,创建了一个指向 Person 类型变量的指针,并将其赋值给变量 p1。然后,我们输出该结构体变量的内容到控制台,并调用函数updatePerson()来修改年龄属性。在函数updatePerson()中,使用结构体指针参数 p 来访问原始结构体变量,并修改其年龄属性。最后,再次输出更新后的结构体变量内容到控制台。执行结果如下:

Before: {Alice 25}
After: {Alice 30}

由此可以看到,在调用函数updatePerson()后,结构体变量的年龄属性已经被更新,并且在主函数 main()中,它的值也发生了变化。这是因为我们使用了结构体指针来操作结构体变量,而不是复制结构体变量本身。

注意结构体指针是 Go 语言中一种非常有用的特性,可以提高程序的效率和灵活性。但是需要注意,如果使用不当,结构体指针可能会导致内存泄漏和其他问题。因此,在使用结构体指针时一定要小心谨慎。

7.3.5  函数返回结构体类型的值

在Go语言中,一个函数可以返回一个函数值,这个函数值可以是整型、实型、字符型、指针型等。另外,Go语言函数还可以返回一个结构体类型的值,即函数的类型可以定义为结构体类型。例如下面是一个以下是一个示例代码的演示代码。

type Person struct {
    Name string
    Age  int
}

func getPerson() Person {
    return Person{Name: "Alice", Age: 25}
}

func main() {
    p := getPerson()
    fmt.Println(p)
}

在上述代码中,首先定义了一个名为 Person 的结构体类型,其中包含一个名为 Name 的字符串和一个名为 Age 的整数。然后定义了一个函数getPerson(),该函数返回一个 Person 类型的变量。在函数内部,使用结构体字面量的方式创建并初始化一个 Person 类型的结构体,并将其作为函数的返回值。最后,在主函数main()中调用函数getPerson(),并将返回值存储在变量 p 中。然后,输出变量 p 的内容到控制台。输出结果如下:

{Alice 25}

由此可见,成功地从函数中返回了一个包含两个属性的结构体类型的值,并在主程序中对其进行了操作。这说明了 Go 语言中函数返回结构体类型值的实用性。

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码农三叔

感谢鼓励

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

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

打赏作者

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

抵扣说明:

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

余额充值