Go语言基础

Go的数据类型定义

Go 复制代码
//运行第一个程序

package main

func main(){
    print("Hello World")
}

在GO语言中,一个程序只能有一个main包,对应只能有一个main方法,若无法满足这个条件,编译时将会报错。注释方式与PHP相同

  1. import的使用

个人在使用import时,按照自己的编写习惯,依赖IDE的自动导包功能,实现包的导入。若导入包后没有使用,会编译出错。

Go 复制代码
// 使用  .  来实现省略调用
import . "fmt"

// 在包前通过别名替代包名
import stdio "fmt"

// 包前使用 _ 作用为只调取包的init方法
import _ "xxx"

// 使用 () 实现多包导入
import (
    "fmt"
    "os"
)
  1. GO的可见性

GO通过大小写来判别函数或者变量的权限,小写则为private,大写为public

  1. 定义数据结构

GO定义数据类型比较有意思。在函数体内使用var定义的变量必须被使用,否则编译报错,申明外部变量没有限制

  1. 布尔类型

布尔类型只有true与false两个值,不可用0与1替代

  1. 整形

int类型根据使用的平台自动判别32位还是64位,可以手动定义指定的位数,例如 int8/uint16,定义了指定8位的int型/16位的无符号int型

  1. 浮点型

浮点型有float32/float64,精确范围对应7/15位

  1. 复数类型

complex64/complex128

  1. 其他值类型

array struct string

  1. 引用类型

slice map chan

  1. 接口类型

inteface

  1. 函数类型

func

Go 复制代码
// 定义单个变量
var hello = "Hello World"

//定义多个变量
var(
	num = 321
    str = "haha"
)

//定义常量,定义多个常量与变量的定义一样
const PI =3.14
  1. 类型零值

零值不等于空值,变量被声明为某种类型后的默认值,通常情况下整型的默认值为0,bool为false,string为空字符串

  1. 类型别名的定义

利用type进行类型别名的定义

Go 复制代码
type(
chinese string
intMax int64
)
  1. 变量声明与赋值

    变量的声明格式:var <name> <type>
    变量的赋值格式:<name> = <value>
    声明的同时赋值:var <name> [type] = <value>
    自身推断赋值:var <name> = <value>
    简写赋值(不可全局变量赋值使用) <name> := <value>

  2. 类型转换

进行类型转换时,需要进行显式转换,并且需要保证两个不同的类型转换可以互相兼容

Go 复制代码
var a = 'a'
b := int(a)
  1. 常量的定义问题

常量值在编译时已经确定,所以右侧的数据需要使用常量或者内置函数,变量不能进行常量的赋值。定义常量组时,如果不进行数值的赋值,将会将其数值赋值为其上行的数值。常量初始化规则定义了每一行的常量定义个数一致时,默认上行数值下行初始化将进行,否则报错。
常量的iota是常量计数器,只在常量的表达式中使用。const中每新增一行常量声明将使iota计数一次

Go 复制代码
// 赋值b,c均为hello字符串,赋值g,h为1,2
const(
	a = "hello"
    b
    c
    e,f = 1,2
    g,h
)

// 输出 c为2 f为5
const(
	a=1
    b
    c = iota
    d
    e
    f = iota
)

运算符

1、运算符的基本介绍

运算符是一种特殊的符号,可以表示数据的运算,赋值,比较等

1)算术运算符

2)赋值运算符

3)比较运算符

4)逻辑运算符

5)位运算符

6)其他运算符

比较两个数的大小。

Go 复制代码
package lee

import "fmt"

func Test(){
    a:=101
    b:=66
    if(a>b){
        fmt.Printf("a大一些")
    }else{
        fmt.Printf("b大一些")
    }
}

注意:go语言不支持三元运算符

2、运算符的优先级

控制语句

  1. if条件判断

if条件判断:在布尔表达式里面可以定义变量,并行赋值.变量的作用域只能作用在代码块里面.

Go 复制代码
// 格式:
if <布尔表达式>{
	语句块
}else if <布尔表达式>{
	语句块
}else{
	语句块
}

// 定义i=5 输出5,下面一句会编译出错
if i:=5;i<10{
	print(i)
}
print(i)
  1. 循环控制

go可以使用for完成所有的循环操作,变量的作用域只能作用在代码块里面

Go 复制代码
//经典for循环结构
for i:=1;i<100;i++{
	print(i)
}

//单条件(while式)for循环结构
for true{
	print("进入死循环")
}

// 无条件式
for {
	print("hello")
}
  1. switch语句

switch不需要使用break,自动进行跳出,若需要连续条件选择,使用fallthrough。

Go 复制代码
// 常规操作
i := 3
switch i{
	case 1:
		print(1)
	case 2:
		print(2)
	case 3:
		print(3)
	default:
		print(4)
}
    
// 在表达式中定义变量
switch i=3; {
	case i==1:
		print(1)
	case i==2:
		print(2)
	case i==3:
		print(3)
	default:
		print(4)
}

// 使用fallthrough  输出2 3
i := 2
switch i{
	case 1:
		print(1)
	case 2:
		print(2)
        fallthrough
	case 3:
		print(3)
	default:
		print(4)
}
  1. 跳转语句

goto break continue 三者均可以执行跳转。

goto用于调整执行位置。使用时,注意标签的位置

break用于循环语句中跳出循环,并开始执行循环之后的语句

continue不是跳出循环,而是跳过当前循环执行下一次循环语句,一般用于for 循环中,触发for增量语句的执行。

Go 复制代码
//goto操作 这里输出   你好,这里是跳转
START:
		print("你好,这里是跳转")
	    os.Exit(1)
	for i:=1; i<3;i++  {
			goto START
		}
		print("这里是测试是否跳转")
        
// break操作 输出   跳出了循环start
START:
	for i:=1;i<=2 ;i++  {
		break START
	}
	print("跳出了循环start")
    
//continue操作   输出  2 4 6 8
for i:=1;i<10 ;i++  {
		if(i%2!=0){
			continue
		}
		print(i)
}

数组

数组的定义格式 var < name > [n]< type >。

数组赋值时,两个不同长度变量无法进行赋值操作。

go语言中 数组是一个值类型,使用时,直接对值进行操作,而不是地址引用。

两个长度相同的数组可以实现等于号判断。

Go 复制代码
//常规定义
var a [5]int
//简写
a:=[2]int{1,2}
//索引赋值:将最后一个元素定义为5 其余为默认值
a:=[20]int{19:5}
//自动判断数组长度定义,使用三个点 编译器将会自动判断长度赋值
a:=[...]int{11:5}
//定义指针
var p *[5]int
//定义多维数组
var arr = [2][3]int{}
arr := [2][3]int{}

//GO语言冒泡排序
package main
import "fmt"
func main(){
	var a = [5]int{5,9,4,1,6}
	num := len(a)
	for i:=0;i<num ;i++  {
		for j:=i+1;j<num ;j++  {
			if(a[i]>a[j]){
				temp:=a[j]
				a[j] = a[i]
				a[i] = temp
			}
		}
	}
	fmt.Print(a)
}

切片

一、切片的定义

在Go语言中,切片(Slice)是数组的一个引用。

它会生成一个指向数组的指针,并通过切片长度关联到底层数组部分或者全部元素。

切片还提供了一系列对数组的管理功能(append、copy),可以随时动态扩充存储空间,并且可以被随意传递而不会导致所管理的数组元素被重复复制。

根据以上特征,切片通常被用来实现变长数组,而且操作灵活。

切片的数据结构原型定义如下:

src/ pkg/ runtime/ runtime. h

Go 复制代码
struct Slice
{                   //must not move anything
    byte* array;    //actual data
    unit32 len;     //number of elements
    unit32 cap;     //allocated number of elements
};

由切片数据结构的原型定义可以看到,它抽象为以下三个部分:

指向被引用的底层数组的指针。

切片中元素的个数。

切片分配的存储空间。

二、切片的声明与创建

切片声明与创建的方法有三种:

1、基于底层数组创建

2、直接创建

3、使用make()函数创建。

1、基于底层数组创建

在创建切片时,可以基于一个底层数组,切片可以只使用数组的一部分元素或所有元素,甚至可以创建一个比底层数组还要大的数组切片,因为切片可以动态增长。创建切片的格式如下:

Go 复制代码
var sliceName [ ]dataType

说明:

切片名的命名规则和变量名相同,遵循标识符命名规则。

在创建切片时,不要指定切片的长度。

切片的类型可以是Go语言的任何基本数据类型。

例如:

var slice1 [] int

上例中定义了一个整型切片slicel,注意不要指定切片长度,如果指定了长度就成了定义数组了。

当一个切片定义好以后,如果还没有被初始化,默认值为nil, 而且切片的长度为0。切片的长度可以使用内置函数len()获取,还可以使用内置函数cap()获取切片的内存容量。

所以,当一个切片定义好以后,还要对切片进行初始化,这样切片才可用。对于上例,假如已经定义了一个整型数组array1,则切片slice1的初始化方式如下:

Go 复制代码
slice1 = array1[start : end]

从上式可以看到,Go语言支持以array1[start : end]的方式基于一个底层数组来生成切片,即切片引用的数组元素由array1[start]到 array1[end],但不包含array1[end]。

如果要引用底层数组的所有元素,可采取的方式如下:

Go 复制代码
slicel = array1
slice1 = array1[ :]
slicel = array1[0: len(array1)] 

2、直接创建切片

直接创建切片,即在定义切片的时候初始化切片元素,例如:

Go 复制代码
var slice1 =[]int{1,2,3,4,5}

3、使用make函数创建切片

内置函数make()用于灵活的创建切片

Go 复制代码
var slicel = make([]int,5)

上式创建了一个有5个元素的整型切片slicel,元素的初值为0。

在使用make()函数创建切片时,还可以为切片元素预留存储空间。例如:

Go 复制代码
var slice1 = make([]int, 5,10)

上式表示,创建整型切片slice1,元素个数为5,元素初值为0,并预留10个元素的存储空间。

三、切片元素的访问与遍历

切片元素的遍历和数组元素的遍历一样,要通过元素下标访问,另外也可以使用关键字range遍历所有切片元素。

切片元素访问的一般格式如下:

Go 复制代码
sliceName [sliceIndex]

遍历同数组,使用range关键字表达式,有两个返回值,第一个是元素的下标,第二个是元素的值。

四、切片的操作

1、切片元素的增加

可以使用append()函数向切片尾部添加新元素,这些元素保存到底层数组。append并不会影响原来切片的属性,它返回变更后新的切片对象。

与数组相比,除了都有长度(length)以外,切片多了一个容量(capacity)的概念,即切片中元素的个数和分配的存储空间是两个不同的值。如果在增加新元素时超出cap的限制,则底层会重新分配一块"够大"的内存,一般来说是将原来的内存空间扩大二倍,然后再将数据从原来的内存复制到新的内存块。

2、切片元素的复制

使用切片长时间引用"超大"的底层数组,会导致严重的内存浪费。

可以新建一个小的slice对象,然后将所需的数据复制过去。

函数copy()可以在切片之间复制元素,能够复制的数量取决于复制方和被复制方的长度值,通常取最小值。

需要注意的是,在同一底层数组的不同切片间复制元素时,元素位置会发生重叠。

Go 复制代码
//切片的复制
package main
 
import(
    "fmt" 
)
 
func main() {
    var slicel= []int{1,2,3,4,5,6,7,8,9,10}
    var slice2 = make([ ] int,3, 5)
    var n int
    //只能复制三个元素
    n= copy(slice2,slice1)
    fmt. Println(n, slice2, len( slice2), cap(slice2))
    //slice3和slice1指向同一底层数组
    slice3 : = slice1[3:6]
    //复制后元素重叠
    n= copy(slice3, slice1[1:5])
    fmt.Println(n, slicel, slice3)
}

编译并运行程序输出结果为:
3 [1 2 3] 3 5
3 [1 2 3 2 3 4 7 8 9 10] [2 3 4]

通过输出结果可以看出,在将slice1复制到slice2时,由于slice2的长度最小为3,所以只能将slice1的前三个元素复制给slice2。而将slicel1复制到slice3 时,由于slicel1和slice3指向同一个底层数组,所以复制后元素重叠。slice3刚创建时,它引用的是底层数组的[4,5,6]三个元素,复制后slice1将[2,3,4]三个元素复制给slice3 ,所以最后slice3 的元素[2,3,4]覆盖了slice1的元素[4,5,6]。

3、排序和搜索切片

标准库中的sort包提供了对整型、浮点型和字符串类型切片进行排序的函数,检查一个切片是否排序好的函数,以及使用二分搜索算法在一个有序切片中搜索-一个元素的函数。 同时提供了通用sort.Sort ()和sort.Search ()函数,可用于任何自定义的数据。

函数

1.简介

  • 函数:为完成某一功能的程序指令(语句)的集合
  • 在Go中,函数分为自定义函数、系统函数

2.基本语法

Go 复制代码
func 函数名(形参列表) (返回值列表) {
    函数体
    return 返回值列表
}

3.递归

  • 函数体内调用自己

    package main

    import "fmt"

    func test(n int) {
    if n > 2 {
    n--
    test(n)
    }
    fmt.Println("n = ", n)
    }

    func main() {
    test(4)
    }

  • 执行一个函数时,就创建一个新的受保护的独立空间

  • 函数的局部变量是独立的,不会相互影响

  • 递归必须向退出递归的条件逼近,否则就会出现无限递归

  • 当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用就将结果返回给谁。

4.递归练习

  • 1.求第N个斐波那契数列对应的值
Go 复制代码
package main

import "fmt"

func feb(n int) int {
    if n <= 2 {
        return 1
    }
    return test(n-1) + test(n-2)
}

func main() {
    result := feb(6)
    fmt.Println(result)
}
  • 2.已知函数f(1) = 3f(n) = 2*f(n-1) + 1,求f(n)的值
Go 复制代码
package main

import "fmt"

func test(n int) int {
    if n == 1 {
        return 3
    }
    return 2*test(n-1) + 1
}

func main() {
    result := test(6)
    fmt.Println(result)
}
  • 3.有一堆桃子,猴子第一天吃了其中的一半,并在多吃了一个!以后每天猴子都吃其中的一半,然后再多吃一个。当到第十天准备吃时,发现只剩了1个桃子问题:最初总共多少个桃子
  • 从上图可以看出其通项公式为:(1 + f(n+1))* 2n代表天数
Go 复制代码
package main

import "fmt"

func getPeach(n int) int {
    if n == 10 {
        return 1
    }
    return (1 + getPeach(n+1)) * 2
}

func main() {
    result := getPeach(1)
    fmt.Println(result)
}

5.函数使用注意细节

  • 1.函数的形参列表和返回值列表都可以是多个

  • 2.形参列表和返回值列表都可以是值类型或引用类型

  • 3.函数命名遵循标识符命名规则,首字母不能是数字,首字母如果是大写,则可以被其他文件或包调用,否则只能在当前文件被调用

  • 4.函数的变量是局部的,函数外不生效

  • 5.基本类型和数组默认都是值传递,即进行值拷贝,在函数内修改,不会影响到原来的值

  • 6.如果希望函数内的变量能修改函数外的变量,可以传递变量的地址&,函数内以指针方式操做变量

  • 7.Go函数不支持重载(即一个包下不允许存在同名的函数)

  • 8.在Go语言中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量,通过该变量可以对函数进行调用

  • 9.函数既然是一种数据类型,因此在Go语言中,函数可以作为形参,并且调用

    Go 复制代码
    package main
    
    import "fmt"
    
    func test(myFunc func(int, int) int, num1, num2 int) int {
        return myFunc(num1, num2)
    }
    
    func getSum(a, b int) int {
        return a + b
    }
    func main() {
        resp := test(getSum, 1, 2)
        fmt.Println(resp)
    }
  • 10.为了简化数据类型定义,Go语言支持自定义数据类型

    • 基本语法:type myInt int,myInt就可以当作int类型来使用,但不完全一致,不能将一个myInt类型的值直接赋值给int类型的变量,需要使用int()来强转一下。
    • 也可以使用type来定义一个函数类型
    Go 复制代码
    package main
    
    import "fmt"
    
    type myFuncType func(int, int) int
    
    func test(myFunc myFuncType, num1, num2 int) int {
        return myFunc(num1, num2)
    }
    
    func getSum(a, b int) int {
        return a + b
    }
    
    func main() {
        resp := test(getSum, 1, 2)
        fmt.Println(resp)
    }
  • 11.支持对函数返回值命名

    Go 复制代码
    package main
    
    import "fmt"
    
    type myFuncType func(int, int) int
    
    func test(myFunc myFuncType, num1, num2 int) int {
        return myFunc(num1, num2)
    }
    
    // 将函数返回值重命名为sum
    func getSum(a, b int) (sum int) {
        sum = a + b
        return
    }
    
    func main() {
        resp := test(getSum, 1, 2)
        fmt.Println(resp)
    }
  • 12.使用_来忽略返回值

  • 13.Go支持可变参数参数名...,必须放在参数最后位置

    Go 复制代码
    package main
    
    import "fmt"
    
    func getSum(args ...int) int {
        sum := 0
        for i := 0; i < len(args); i++ {
            sum += args[i]
        }
        return sum
    }
    func main() {
        resp := getSum(1, 2, 3)
        fmt.Println(resp)
    }

6.init函数

  • 每个源文件都可以包含一个init函数,该函数会在main函数前执行,被Go运行框架调用。

  • 通常在init函数中完成初始化工作,每个文件都可以有一个init函数

    Go 复制代码
    package main
    
    import "fmt"
    
    func init() {
        fmt.Println("init函数自动执行")
    }
    
    func main() {
        fmt.Println("开始执行主函数")
    }
  • 如果一个文件中同时包含全局变量init函数main函数,则执行顺序为:全局变量 > init > main

    Go 复制代码
    package main
    
    import "fmt"
    
    var money = test()
    
    func test() int {
        fmt.Println("test()")
        return 100
    }
    
    func init() {
        fmt.Println("init函数自动执行")
    }
    
    func main() {
        fmt.Println("开始执行主函数...money=", money)
    }
    
    // test()
    // init函数自动执行
    // 开始执行主函数...money= 100

7.匿名函数

Go支持匿名函数,如果我们某个函数只希望使用一次,可以考虑使用匿名函数,匿名函数可以实现多次调用

  • 示例一:在定义时就调用

    Go 复制代码
    package main
    
    import "fmt"
    
    func main() {
        res := func(n1, n2 int) int {
            return n1 + n2
        }(10, 20)
        fmt.Println("result =", res)
    }
  • 示例二:将匿名函数赋值给一个变量(函数变量),再通过该变量来调用匿名函数

    Go 复制代码
    package main
    
    import "fmt"
    
    func main() {
        sum := func(n1, n2 int) int {
            return n1 + n2
        }
    
        res := sum(10, 20)
        fmt.Println("result =", res)
    }
  • 示例三:全匿名函数,将函数赋值给一个全局变量,就成为一个全局函数,可以在程序有效

    Go 复制代码
    package main
    
    import "fmt"
    
    var multi = func(n1, n2 int) int {
        return n1 * n2
    }
    
    func main() {
        res := multi(10, 20)
        fmt.Println("result =", res)
    }

8.闭包

  • 闭包就是一个函数与其相关的引用环境组合的一个整体

  • 闭包必须满足三个条件:

    1、必须有一个内嵌函数

    2、内嵌函数必须引用外部函数中的变量

    3、外部函数返回值必须是内嵌函数

  • 闭包可以使得变量常驻内存

    Go 复制代码
    package main
    
    import "fmt"
    
    func Closure() func(int) int {
        var n1 = 10
        return func(n2 int) int {
            n1 = n1 + n2
            return n1
    
            // return n1 + n2  // 这样的结果完全不一样, n1 = n1 + n2会修改局部变量n1的值,如果直接返回n1 + n2,则不会修改局部变量n1的值
        }
    }
    
    func main() {
        res := Closure()
        fmt.Println("result =", res(30)) // 40
        fmt.Println("result =", res(31)) // 71
    }
  • nodejs

    javascript 复制代码
    function closure() { 
        var a = 10; 
        function inner(b) {
            a = a + b;  
            return a
        } 
        return inner;
    }
    var func = closure();
    console.log(func(30));  // 40
    console.log(func(31));  // 71
  • python

    python 复制代码
    # 变量 a 对于 inner来说是外部变量,因此不能直接进行修改
    def closure():
        a = 10
        def inner(b):
            a = a + b
            return a
        return inner
    
    # UnboundLocalError: local variable 'a' referenced before assignment
    
    # 但对于inner来说是可以直接使用的
    def closure():
        a = 10
        def inner(b):
            return a + b
        return inner
    
    # 必须使用 nonlocal 来修改变量a的作用域,从而对其进行操做
    def closure():
        a = 10
        def inner(b):
            nonlocal a
            a = a + b
            return a
        return inner
    
    func = closure()
    print(func(30))  # 40
    print(func(31))  # 71
  • 案例:使用闭包方式,实现检查文件后缀名是否为.jpg,如果不是,则内部实现拼接.jpg,否则直接返回

    Go 复制代码
    package main
    
    import (
    	"fmt"
    	"strings"
    )
    
    func Closure(endFix string) func(string) string {
        return func(name string) string {
            if strings.HasSuffix(name, endFix) {
                return name
            }
            return name + endFix
        }
    }
    
    func main() {
        res := Closure(".jpg")
        fmt.Println("result =", res("aaa.jpg"))
        fmt.Println("result =", res("bbb"))
    }

9.函数参数传递

  • 值类型:基本数据类型:int系列、float系列、bool、string、数组、结构体
  • 引用类型:指针、slice切片、map、管道chan、interface等

结构体

结构体是一种用户自定义的数据类型,它由一组字段组成,每个字段可以是任意基本类型或其他结构体类型。结构体在Go语言中被广泛使用,它可以用来表示复杂的数据结构,比如二叉树、链表、图等。

一个简单的结构体定义如下:

Go 复制代码
copy codetype Person struct {
    name string
    age int
    gender string
}

上面的代码定义了一个名为Person的结构体,它有三个字段,分别是name、age和gender,它们的类型分别为string、int和string。我们可以通过该结构体来表示一个人的基本信息。

1.结构体的语法

Go语言中结构体的语法非常简洁,它的基本形式如下:

Go 复制代码
copy codetype 结构体名 struct {
    字段1 类型1
    字段2 类型2
    ...
    字段n 类型n
}

其中,结构体名是用户自定义的标识符,用于表示该结构体的类型。字段是结构体中的成员,每个字段都有一个对应的类型。在定义结构体时,可以使用任何基本类型或其他结构体类型作为字段的类型。另外,结构体中的字段可以使用大写字母开头的标识符表示公有成员,小写字母开头的标识符表示私有成员。

2.结构体的初始化

在Go语言中,结构体类型的变量可以通过结构体字面量来进行初始化。结构体字面量是一种简洁的语法,可以用于快速创建一个结构体类型的变量。

结构体字面量的基本形式如下:

Go 复制代码
copy codevar 变量名 = 结构体类型{
    字段1: 值1,
    字段2: 值2,
    ...
    字段n: 值n,
}

其中,变量名是结构体类型的变量名,结构体类型是用户自定义的结构体类型,字段是结构体中的成员,值是该成员对应的初始化值。我们可以省略字段名,只提供值,此时Go语言会按照结构体定义时的字段顺序来进行初始化。例如:

Go 复制代码
copy codevar p1 = Person{
    "张三",
    18,
    "男",
}

var p2 = Person{
    name: "李四",
    age: 20,
    gender: "女",
}

上面的代码分别创建了两个Person类型的变量p1和p2,它们的字段值分别为{张三 18 男}和{李四 20 女}。可以看出,结构体字面量提供了一种简单、直观的方式来初始化结构体类型的变量。

3.结构体的访问

在Go语言中,可以通过结构体变量的字段名来访问结构体中的成员。例如:

Go 复制代码
copy codevar p Person
p.name = "张三"
p.age = 18
p.gender = "男"

上面的代码创建了一个名为p的Person类型的变量,并对其成员进行了赋值。可以看出,访问结构体成员的语法非常简单,只需要使用点号(.)连接结构体变量和成员名即可。

4.结构体的方法

Go语言中的结构体还支持方法,方法是一种特殊的函数,它可以与结构体类型关联。方法与函数的区别在于,方法必须在结构体类型上定义,而函数可以在任何地方定义。另外,方法可以访问结构体的成员,而函数只能访问其参数。

Go语言中的方法定义如下:

Go 复制代码
copy codefunc (接收者变量 接收者类型) 方法名(参数列表) 返回值列表 {
    // 方法体
}

其中,接收者变量是一个标识符,用于表示方法所属的结构体类型的变量。接收者类型是方法所属的结构体类型,参数列表和返回值列表与普通函数的定义方式相同。

下面是一个例子:

Go 复制代码
copy codetype Rectangle struct {
    width, height float64
}

func (r Rectangle) Area() float64 {
    return r.width * r.height
}

func main() {
    r := Rectangle{3, 4}
    fmt.Println("面积:", r.Area())
}

上面的代码定义了一个名为Rectangle的结构体,它有两个字段width和height。该结构体还定义了一个名为Area的方法,用于计算矩形的面积。

在main函数中,创建了一个Rectangle类型的变量r,并调用其Area方法来计算面积。

5.结构体的嵌套

在Go语言中,结构体可以嵌套在其他结构体中,从而形成更复杂的数据结构。嵌套结构体的定义方式与普通结构体的定义方式相同,只是在字段的类型中指定另一个结构体类型。

下面是一个例子:

Go 复制代码
copy codetype Address struct {
    Province string
    City string
}

type Person struct {
    Name string
    Age int
    Address Address
}

func main() {
    p := Person{
        Name: "张三",
        Age: 18,
        Address: Address{
            Province: "广东省",
            City: "深圳市",
        },
    }

    fmt.Println(p)
}

上面的代码定义了两个结构体类型,Address和Person。Person结构体中包含一个Address类型的字段,用于表示该人员的地址信息。

在main函数中,创建了一个Person类型的变量p,并对其成员进行了赋值。可以看出,嵌套结构体提供了一种简单、灵活的方式来表示复杂的数据结构。

6.结构体的匿名字段

在Go语言中,结构体还支持匿名字段。匿名字段是一种特殊的字段类型,它没有字段名,只有字段类型。在结构体中,可以使用匿名字段来表示继承关系或者组合关系。

下面是一个例子:

Go 复制代码
copy codetype Person struct {
    string
    int
}

func main() {
    p := Person{"张三", 18}
    fmt.Println(p.string, p.int)
}

上面的代码定义了一个Person结构体,它包含了一个string类型的匿名字段和一个int类型的匿名字段。在main函数中,创建了一个Person类型的变量p,并对其成员进行了访问。可以看出,使用匿名字段可以在一定程度上简化代码。

7.结构体的标签

在Go语言中,结构体的字段可以使用标签(tag)来进行注释或者元数据的传递。标签是一个字符串,它的格式为key:"value",其中key表示标签的名称,value表示标签的值。

下面是一个例子:

Go 复制代码
copy codetype Person struct {
    Name string `json:"name"`
    Age int `json:"age"`
}

func main() {
    p := Person{"张三", 18}
    b, _ := json.Marshal(p)
    fmt.Println(string(b))
}

上面的代码定义了一个Person结构体,它有两个字段Name和Age。在Name字段和Age字段上,我们使用了json标签,用于指定json序列化时使用的字段名。

在main函数中,创建了一个Person类型的变量p,并将其序列化为json字符串。可以看出,使用标签可以方便地进行元数据传递和注释。

8.结构体的应用场景

在Go语言中,结构体被广泛应用于各种数据结构的定义和实现。下面是一些常见的应用场景:

  • 数据库ORM:结构体可以与数据库表进行映射,从而实现对象关系映射(ORM)功能。
  • 网络编程:结构体可以用于表示网络数据包的格式,从而实现网络编程功能。
  • 并发编程:结构体可以用于实现并发编程模型,例如管道、锁和条件变量等。
  • 配置文件:结构体可以用于表示配置文件的格式,从而实现配置文件读写功能。
  • 模板引擎:结构体可以用于表示模板数据,从而实现模板引擎功能。
相关推荐
m0_7482567816 分钟前
【Django自学】Django入门:如何使用django开发一个web项目(非常详细)
前端·django·sqlite
林小白的日常27 分钟前
uniapp中wx.getFuzzyLocation报错如何解决
前端·javascript·uni-app
傻小胖1 小时前
React 脚手架配置代理完整指南
前端·react.js·前端框架
EterNity_TiMe_1 小时前
【论文复现】农作物病害分类(Web端实现)
前端·人工智能·python·机器学习·分类·数据挖掘
余生H1 小时前
深入理解HTML页面加载解析和渲染过程(一)
前端·html·渲染
吴敬悦2 小时前
领导:按规范提交代码conventionalcommit
前端·程序员·前端工程化
ganlanA2 小时前
uniapp+vue 前端防多次点击表单,防误触多次请求方法。
前端·vue.js·uni-app
卓大胖_2 小时前
Next.js 新手容易犯的错误 _ 性能优化与安全实践(6)
前端·javascript·安全
m0_748246352 小时前
Spring Web MVC:功能端点(Functional Endpoints)
前端·spring·mvc
CodeClimb2 小时前
【华为OD-E卷 - 猜字谜100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od